Skip to content

Commit

Permalink
Things from #354: Keep "_ColorRange" value on ConvertToRGBxx for "PC.…
Browse files Browse the repository at this point in the history
…709" or "PC.601";

Recognize studio rgb source (_ColorRange=1 limited RGB) in Greyscale and ConvertToYxx
  • Loading branch information
pinterf committed Oct 19, 2023
1 parent 71188c8 commit 184eebe
Show file tree
Hide file tree
Showing 20 changed files with 791 additions and 314 deletions.
30 changes: 15 additions & 15 deletions avs_core/convert/convert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,22 +100,21 @@ extern const AVSFunction Convert_filters[] = { // matrix can be "rec601",
******* Convert to RGB / RGBA ******
***************************************/

// YUY2 only
// YUY2 to packed RGB only
ConvertToRGB::ConvertToRGB( PClip _child, bool rgb24, const char* matrix_name,
IScriptEnvironment* env )
: GenericVideoFilter(_child)
{
auto frame0 = _child->GetFrame(0, env);
const AVSMap* props = env->getFramePropsRO(frame0);
matrix_parse_merge_with_props(vi, matrix_name, props, theMatrix, theColorRange, env);
matrix_parse_merge_with_props(vi.IsRGB(), true, matrix_name, props, theMatrix, theColorRange, theOutColorRange, env);

const int shift = 16; // for integer arithmetic; YUY2 is using 16 bits, later is divided back by 4 or 8
const int bits_per_pixel = 8; // YUY2
if (!do_BuildMatrix_Yuv2Rgb(theMatrix, theColorRange, shift, bits_per_pixel, /*ref*/matrix))
if (!do_BuildMatrix_Yuv2Rgb(theMatrix, theColorRange, theOutColorRange, shift, bits_per_pixel, /*ref*/matrix))
env->ThrowError("ConvertToRGB: invalid \"matrix\" parameter");

theOutMatrix = Matrix_e::AVS_MATRIX_RGB;
theOutColorRange = ColorRange_e::AVS_RANGE_FULL;

// these constants are used with intentional minus operator in core calculations
matrix.v_g = -matrix.v_g;
Expand All @@ -126,16 +125,17 @@ ConvertToRGB::ConvertToRGB( PClip _child, bool rgb24, const char* matrix_name,
}

template<int rgb_size>
static void convert_yuy2_to_rgb_c(const BYTE *srcp, BYTE* dstp, int src_pitch, int dst_pitch, int height, int width, int crv, int cgv, int cgu, int cbu, int cy, int tv_scale) {
static void convert_yuy2_to_rgb_c(const BYTE *srcp, BYTE* dstp, int src_pitch, int dst_pitch, int height, int width, int crv, int cgv, int cgu, int cbu, int cy, int tv_scale, int rgb_offset) {
srcp += height * src_pitch;
rgb_offset <<= 16; // integer arithmetic range
for (int y = height; y > 0; --y) {
srcp -= src_pitch;
int x;
for (x = 0; x < width-2; x+=2) {
int scaled_y0 = (srcp[x*2+0] - tv_scale) * cy;
int scaled_y0 = (srcp[x*2+0] - tv_scale) * cy + rgb_offset;
int u0 = srcp[x*2+1]-128;
int v0 = srcp[x*2+3]-128;
int scaled_y1 = (srcp[x*2+2] - tv_scale) * cy;
int scaled_y1 = (srcp[x*2+2] - tv_scale) * cy + rgb_offset;
int u1 = srcp[x*2+5]-128;
int v1 = srcp[x*2+7]-128;

Expand All @@ -153,8 +153,8 @@ static void convert_yuy2_to_rgb_c(const BYTE *srcp, BYTE* dstp, int src_pitch, i
}
}

int scaled_y0 = (srcp[x*2+0] - tv_scale) * cy;
int scaled_y1 = (srcp[x*2+2] - tv_scale) * cy;
int scaled_y0 = (srcp[x*2+0] - tv_scale) * cy + rgb_offset;
int scaled_y1 = (srcp[x*2+2] - tv_scale) * cy + rgb_offset;
int u = srcp[x*2+1]-128;
int v = srcp[x*2+3]-128;

Expand Down Expand Up @@ -196,21 +196,21 @@ PVideoFrame __stdcall ConvertToRGB::GetFrame(int n, IScriptEnvironment* env)
if (env->GetCPUFlags() & CPUF_SSE2) {
if (vi.IsRGB32()) {
convert_yuy2_to_rgb_sse2<4>(srcp, dstp, src_pitch, dst_pitch, vi.height, vi.width,
matrix.v_r, matrix.v_g, matrix.u_g, matrix.u_b, matrix.y_r, tv_scale);
matrix.v_r, matrix.v_g, matrix.u_g, matrix.u_b, matrix.y_r, tv_scale, matrix.offset_rgb);
} else {
convert_yuy2_to_rgb_sse2<3>(srcp, dstp, src_pitch, dst_pitch, vi.height, vi.width,
matrix.v_r, matrix.v_g, matrix.u_g, matrix.u_b, matrix.y_r, tv_scale);
matrix.v_r, matrix.v_g, matrix.u_g, matrix.u_b, matrix.y_r, tv_scale, matrix.offset_rgb);
}
}
else
#ifdef X86_32
if (env->GetCPUFlags() & CPUF_INTEGER_SSE) {
if (vi.IsRGB32()) {
convert_yuy2_to_rgb_isse<4>(srcp, dstp, src_pitch, dst_pitch, vi.height, vi.width,
matrix.v_r, matrix.v_g, matrix.u_g, matrix.u_b, matrix.y_r, tv_scale);
matrix.v_r, matrix.v_g, matrix.u_g, matrix.u_b, matrix.y_r, tv_scale, matrix.offset_rgb);
} else {
convert_yuy2_to_rgb_isse<3>(srcp, dstp, src_pitch, dst_pitch, vi.height, vi.width,
matrix.v_r, matrix.v_g, matrix.u_g, matrix.u_b, matrix.y_r, tv_scale);
matrix.v_r, matrix.v_g, matrix.u_g, matrix.u_b, matrix.y_r, tv_scale, matrix.offset_rgb);
}
}
else
Expand All @@ -219,10 +219,10 @@ PVideoFrame __stdcall ConvertToRGB::GetFrame(int n, IScriptEnvironment* env)
{
if (vi.IsRGB32()) {
convert_yuy2_to_rgb_c<4>(srcp, dstp, src_pitch, dst_pitch, vi.height, vi.width,
matrix.v_r, matrix.v_g, matrix.u_g, matrix.u_b, matrix.y_r, tv_scale);
matrix.v_r, matrix.v_g, matrix.u_g, matrix.u_b, matrix.y_r, tv_scale, matrix.offset_rgb);
} else {
convert_yuy2_to_rgb_c<3>(srcp, dstp, src_pitch, dst_pitch, vi.height, vi.width,
matrix.v_r, matrix.v_g, matrix.u_g, matrix.u_b, matrix.y_r, tv_scale);
matrix.v_r, matrix.v_g, matrix.u_g, matrix.u_b, matrix.y_r, tv_scale, matrix.offset_rgb);
}
}
return dst;
Expand Down
82 changes: 67 additions & 15 deletions avs_core/convert/convert_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ bool getColorRange(const char* color_range_name, IScriptEnvironment* env, int& _

// Converts old-style Avisynth matrix to separated _Matrix and _ColorRange values
// if not found or empty, returns false, does not throw exception.
// _ColorRange for matrix generation
// _ColorRange_Out for desired output range, can be set even to nothing for PC.xxx matrices
static bool getOldMatrix(const char* matrix_name, int &_Matrix, int &_ColorRange) {
auto old_matrix_enum = lookup_table<Old_Avs_Matrix_e>(g_old_avs_matrix_table, matrix_name);
if (old_matrix_enum < 0)
Expand All @@ -224,27 +226,27 @@ static bool getOldMatrix(const char* matrix_name, int &_Matrix, int &_ColorRange
break;
case Old_Avs_Matrix_e::AVS_OLD_MATRIX_PC_601:
_Matrix = Matrix_e::AVS_MATRIX_ST170_M;
_ColorRange = ColorRange_e::AVS_RANGE_FULL;
_ColorRange = -1; // better no change the range ColorRange_e::AVS_RANGE_FULL;
break;
case Old_Avs_Matrix_e::AVS_OLD_MATRIX_Rec709:
_Matrix = Matrix_e::AVS_MATRIX_BT709;
_ColorRange = ColorRange_e::AVS_RANGE_LIMITED;
break;
case Old_Avs_Matrix_e::AVS_OLD_MATRIX_PC_709:
_Matrix = Matrix_e::AVS_MATRIX_BT709;
_ColorRange = ColorRange_e::AVS_RANGE_FULL;
_ColorRange = -1; // better no change the range ColorRange_e::AVS_RANGE_FULL;
break;
case Old_Avs_Matrix_e::AVS_OLD_MATRIX_Rec2020:
_Matrix = Matrix_e::AVS_MATRIX_BT2020_NCL;
_ColorRange = ColorRange_e::AVS_RANGE_LIMITED;
break;
case Old_Avs_Matrix_e::AVS_OLD_MATRIX_PC_2020:
_Matrix = Matrix_e::AVS_MATRIX_BT2020_NCL;
_ColorRange = ColorRange_e::AVS_RANGE_FULL;
_ColorRange = -1; // better no change the range ColorRange_e::AVS_RANGE_FULL;
break;
case Old_Avs_Matrix_e::AVS_OLD_MATRIX_AVERAGE:
_Matrix = Matrix_e::AVS_MATRIX_AVERAGE; // non-standard!
_ColorRange = ColorRange_e::AVS_RANGE_FULL;
_ColorRange = -1;
break;
}
return true;
Expand All @@ -255,6 +257,10 @@ static bool is_paramstring_auto(const std::string &param) {
return !lstrcmpi(param.c_str(), "auto"); // true is match
}

static bool is_paramstring_same(const std::string& param) {
return !lstrcmpi(param.c_str(), "same"); // true is match
}

static bool is_paramstring_empty_or_auto(const std::string& param) {
return param.empty() || !lstrcmpi(param.c_str(), "auto"); // true is match
}
Expand All @@ -266,19 +272,22 @@ static bool is_paramstring_empty_or_auto(const char* param) {
}

// called from yuv <-> rgb and to_greyscale converters
void matrix_parse_merge_with_props_def(VideoInfo& vi, const char* matrix_name, const AVSMap* props, int& _Matrix, int& _ColorRange, int _Matrix_Default, int _ColorRange_Default, IScriptEnvironment* env) {

void matrix_parse_merge_with_props_def(bool rgb_in, bool rgb_out, const char* matrix_name, const AVSMap* props, int& _Matrix, int& _ColorRange, int& _ColorRange_Out, int _Matrix_Default, int _ColorRange_Default, IScriptEnvironment* env) {
bool has_colorrange_property = false;
// if once we'd like to use input colorrange when input is rgb (e.g. studio rgb of ColorBars is limited)
int _ColorRange_In = vi.IsRGB() ? ColorRange_e::AVS_RANGE_FULL : ColorRange_e::AVS_RANGE_LIMITED;
int _ColorRange_In = rgb_in ? ColorRange_e::AVS_RANGE_FULL : ColorRange_e::AVS_RANGE_LIMITED;
int _Default_ColorRange_Out = rgb_out ? ColorRange_e::AVS_RANGE_FULL : ColorRange_e::AVS_RANGE_LIMITED; // RGB -> YUV or YUV -> RGB

if (props) {
// theoretically props==nullptr when input is RGB
// _ColorRange exists for RGB as well
if (env->propNumElements(props, "_ColorRange") > 0) {
has_colorrange_property = true;
_ColorRange_In = (int)env->propGetInt(props, "_ColorRange", 0, nullptr); // fixme: range check
if (!vi.IsRGB())
_ColorRange_Default = _ColorRange_In;
_ColorRange_Default = _ColorRange_In;
if (rgb_in && rgb_out) // rgb in and out: keep input
_Default_ColorRange_Out = _ColorRange_In;
}
if (!vi.IsRGB()) {
if (!rgb_in) {
if (env->propNumElements(props, "_Matrix") > 0) {
int tmp_matrix = (int)env->propGetInt(props, "_Matrix", 0, nullptr); // fixme: range check
if (tmp_matrix != Matrix_e::AVS_MATRIX_UNSPECIFIED)
Expand All @@ -305,7 +314,28 @@ void matrix_parse_merge_with_props_def(VideoInfo& vi, const char* matrix_name, c
std::string s_matrix_name = splits.size() > 0 ? splits[0] : "";
std::string s_color_range_name = splits.size() > 1 ? splits[1] : "";

// PC.xxx is like xxx:same (same is not supported though)
// recxxx is like xxx:l (limited)
if (getOldMatrix(s_matrix_name.c_str(), _Matrix, _ColorRange)) {
// old avs syntax: single matrix names
if (_ColorRange < 0) {
// PC.xx does not suggest neither limited, not full
// keep input range
_ColorRange = _ColorRange_In; // a default range is set for getting the matrix
_ColorRange_Out = _ColorRange_In;
} else {
// _ColorRange was explicitely set by matrix
if (rgb_in) {
// RGB -> YUV
_ColorRange_Out = _ColorRange;
_ColorRange = _ColorRange_In; // a frame prop may override default RGB full input range
}
else {
// YUV -> RGB
// _ColorRange; // input range is defined by the matrix
_ColorRange_Out = _Default_ColorRange_Out;
}
}
if (!s_color_range_name.empty() && !is_paramstring_auto(s_color_range_name))
env->ThrowError("Error: this 'old-style' matrix string can only be followed by 'auto' color range");
return;
Expand All @@ -317,15 +347,37 @@ void matrix_parse_merge_with_props_def(VideoInfo& vi, const char* matrix_name, c
if (_Matrix == Matrix_e::AVS_MATRIX_UNSPECIFIED) {
_Matrix = _Matrix_Default;
}
if (is_paramstring_empty_or_auto(s_color_range_name) || !getColorRange(s_color_range_name.c_str(), env, _ColorRange)) {
_ColorRange = _ColorRange_Default;

// new in 3.7.3: same range as input's, e.g. "709:same" is like "PC.709"
if (is_paramstring_same(s_color_range_name)) {
_ColorRange = _ColorRange_In;
_ColorRange_Out = _ColorRange;
}
else if (is_paramstring_empty_or_auto(s_color_range_name) || !getColorRange(s_color_range_name.c_str(), env, _ColorRange)) {
// not specified or auto
// RGB -> YUV
// YUV -> RGB
_ColorRange = _ColorRange_In;
_ColorRange_Out = _Default_ColorRange_Out;
}
else {
if (rgb_in) {
// RGB -> YUV
_ColorRange_Out = _ColorRange;
_ColorRange = _ColorRange_In; // a frame prop may override default RGB full input range
}
else {
// YUV -> RGB
// _ColorRange; // input range is defined by the matrix
_ColorRange_Out = _Default_ColorRange_Out; // unlike RGB->YUV, we have no way to tell output rgb's range, it's always full
}
}
}

void matrix_parse_merge_with_props(VideoInfo& vi, const char* matrix_name, const AVSMap* props, int& _Matrix, int& _ColorRange, /* int& ColorRange_In, */ IScriptEnvironment * env) {
void matrix_parse_merge_with_props(bool rgb_in, bool rgb_out, const char* matrix_name, const AVSMap* props, int& _Matrix, int& _ColorRange, int& ColorRange_Out, IScriptEnvironment * env) {
int _Matrix_Default = Matrix_e::AVS_MATRIX_ST170_M; // Rec601 AVS_MATRIX_ST170_M (6-NTSC) and not AVS_MATRIX_BT470_BG (5-PAL)
int _ColorRange_Default = ColorRange_e::AVS_RANGE_LIMITED;
matrix_parse_merge_with_props_def(vi, matrix_name, props, _Matrix, _ColorRange, _Matrix_Default, _ColorRange_Default, env);
matrix_parse_merge_with_props_def(rgb_in, rgb_out, matrix_name, props, _Matrix, _ColorRange, ColorRange_Out, _Matrix_Default, _ColorRange_Default, env);
}

void chromaloc_parse_merge_with_props(VideoInfo& vi, const char* chromaloc_name, const AVSMap* props, int& _ChromaLocation, int _ChromaLocation_Default, IScriptEnvironment* env) {
Expand Down
4 changes: 2 additions & 2 deletions avs_core/convert/convert_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ typedef enum Primaries_e {
AVS_PRIMARIES_EBU3213_E = 22
} Primaries_e;

void matrix_parse_merge_with_props(VideoInfo &vi, const char* matrix_name, const AVSMap* props, int& _Matrix, int& _ColorRange, IScriptEnvironment* env);
void matrix_parse_merge_with_props_def(VideoInfo& vi, const char* matrix_name, const AVSMap* props, int& _Matrix, int& _ColorRange, int _Matrix_Default, int _ColorRange_Default, IScriptEnvironment* env);
void matrix_parse_merge_with_props(bool rgb_in, bool rgb_out, const char* matrix_name, const AVSMap* props, int& _Matrix, int& _ColorRange, int& ColorRange_Out, IScriptEnvironment* env);
void matrix_parse_merge_with_props_def(bool rgb_in, bool rgb_out, const char* matrix_name, const AVSMap* props, int& _Matrix, int& _ColorRange, int& ColorRange_Out, int _Matrix_Default, int _ColorRange_Default, IScriptEnvironment* env);
void chromaloc_parse_merge_with_props(VideoInfo& vi, const char* chromaloc_name, const AVSMap* props, int& _ChromaLocation, int _ChromaLocation_Default, IScriptEnvironment* env);

void update_Matrix_and_ColorRange(AVSMap* props, int theMatrix, int theColorRange, IScriptEnvironment* env);
Expand Down
Loading

0 comments on commit 184eebe

Please sign in to comment.