diff --git a/Cargo.toml b/Cargo.toml index 27271de..632fbac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dcv-color-primitives" -version = "0.3.1" +version = "0.4.0" edition = "2018" description = "a library to perform image color model conversion" license = "MIT-0" @@ -14,6 +14,9 @@ categories = ["graphics"] name = "dcv_color_primitives" crate-type = ["staticlib", "rlib"] +[dependencies] +paste = "1.0" + [dev-dependencies] criterion = "0.3" itertools = "0.10" diff --git a/NEWS b/NEWS index d0026ac..159e4e1 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,9 @@ +News in 0.4.0 +------------- +* YCbCr full range support +* Fixed C bindings documentation +* Fixed C bindings unit tests + News in 0.3.1 ------------- * Fix build issue by linking bcrypt diff --git a/README.md b/README.md index fe86f0a..57478aa 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ The supported color models are: * YCbCr, ITU-R Recommendation BT.601 (standard video system) * YCbCr, ITU-R Recommendation BT.709 (CSC systems) +Both standard range (0-235) and full range (0-255) are supported. + ## Requirements * Rust 1.55 and newer (until DCP 0.2: At least Rust 1.39) @@ -427,8 +429,8 @@ default build. In order to include DCV Color Primitives inside your application library, you need to: * Statically link to dcv_color_primitives -* Link to ws2_32.lib and userenv.lib, for Windows -* Link to dl, for Linux +* Link to ws2_32.lib, userenv.lib and bcrypt.lib, for Windows +* Link to libdl and libm, for Linux The API is slightly different than the rust one. Check dcv_color_primitives.h for examples and further information. diff --git a/c_tests/unit.c b/c_tests/unit.c index 0fd8fae..adaacd9 100644 --- a/c_tests/unit.c +++ b/c_tests/unit.c @@ -150,6 +150,9 @@ typedef struct { uint8_t b; } rgb; +typedef uint8_t uint8_mat8_t[8][8]; +typedef uint8_t uint8_mat4_t[4][4]; + static const rgba rgb_to_yuv_input[8][8] = { { { 161, 24, 44, 58 }, @@ -226,7 +229,7 @@ static const rgba rgb_to_yuv_input[8][8] = { } }; -static const uint8_t rgb_to_yuv_y_bt601_output[8][8] = { +static const uint8_mat8_t rgb_to_yuv_y_bt601_output = { { 74, 78, 101, 133, 118, 135, 87, 206 }, { 93, 171, 116, 94, 102, 100, 171, 122 }, { 141, 74, 200, 214, 98, 132, 65, 147 }, @@ -237,7 +240,7 @@ static const uint8_t rgb_to_yuv_y_bt601_output[8][8] = { { 131, 65, 48, 76, 129, 162, 117, 141 }, }; -static const uint8_t rgb_to_yuv_cb_bt601_output[8][8] = { +static const uint8_mat8_t rgb_to_yuv_cb_bt601_output = { { 116, 118, 204, 91, 136, 159, 97, 100 }, { 99, 51, 179, 130, 161, 99, 117, 75 }, { 63, 120, 143, 89, 147, 155, 184, 64 }, @@ -248,7 +251,7 @@ static const uint8_t rgb_to_yuv_cb_bt601_output[8][8] = { { 184, 203, 185, 98, 140, 47, 196, 92 }, }; -static const uint8_t rgb_to_yuv_cr_bt601_output[8][8] = { +static const uint8_mat8_t rgb_to_yuv_cr_bt601_output = { { 187, 105, 177, 198, 208, 103, 119, 135 }, { 229, 162, 147, 87, 79, 221, 118, 111 }, { 187, 86, 115, 119, 141, 163, 166, 169 }, @@ -259,21 +262,21 @@ static const uint8_t rgb_to_yuv_cr_bt601_output[8][8] = { { 157, 133, 120, 122, 186, 153, 65, 155 }, }; -static const uint8_t rgb_to_yuv_cb2_bt601_output[4][4] = { +static const uint8_mat4_t rgb_to_yuv_cb2_bt601_output = { { 96, 151, 139, 97 }, { 119, 130, 135, 153 }, { 110, 143, 146, 73 }, { 135, 152, 116, 140 }, }; -static const uint8_t rgb_to_yuv_cr2_bt601_output[4][4] = { +static const uint8_mat4_t rgb_to_yuv_cr2_bt601_output = { { 171, 152, 153, 121 }, { 141, 124, 118, 127 }, { 145, 132, 135, 117 }, { 145, 129, 135, 126 }, }; -static const uint8_t rgb_to_yuv_y_bt709_output[8][8] = { +static const uint8_mat8_t rgb_to_yuv_y_bt709_output = { { 63, 84, 82, 123, 101, 137, 93, 208 }, { 75, 173, 106, 103, 108, 84, 174, 132 }, { 136, 84, 201, 221, 93, 121, 51, 146 }, @@ -284,18 +287,18 @@ static const uint8_t rgb_to_yuv_y_bt709_output[8][8] = { { 118, 56, 43, 80, 116, 166, 122, 139 }, }; -static const uint8_t rgb_to_yuv_cb_bt709_output[8][8] = { - {123, 115, 211, 98, 145, 156, 95, 100 }, - {110, 53, 182, 126, 156, 109, 116, 72 }, - {68, 115, 141, 87, 149, 160, 189, 67 }, - {122, 174, 130, 160, 128, 98, 181, 174 }, - {200, 97, 186, 96, 192, 136, 77, 46 }, - {53, 95, 209, 84, 182, 78, 56, 107 }, - {95, 60, 118, 212, 76, 197, 129, 148 }, - {189, 205, 185, 97, 147, 48, 190, 94 }, +static const uint8_mat8_t rgb_to_yuv_cb_bt709_output = { + { 123, 115, 211, 98, 145, 156, 95, 100 }, + { 110, 53, 182, 126, 156, 109, 116, 72 }, + { 68, 115, 141, 87, 149, 160, 189, 67 }, + { 122, 174, 130, 160, 128, 98, 181, 174 }, + { 200, 97, 186, 96, 192, 136, 77, 46 }, + { 53, 95, 209, 84, 182, 78, 56, 107 }, + { 95, 60, 118, 212, 76, 197, 129, 148 }, + { 189, 205, 185, 97, 147, 48, 190, 94 }, }; -static const uint8_t rgb_to_yuv_cr_bt709_output[8][8] = { +static const uint8_mat8_t rgb_to_yuv_cr_bt709_output = { { 187, 103, 184, 197, 211, 104, 116, 133 }, { 229, 157, 151, 86, 80, 221, 117, 106 }, { 184, 84, 116, 115, 143, 166, 171, 165 }, @@ -306,20 +309,114 @@ static const uint8_t rgb_to_yuv_cr_bt709_output[8][8] = { { 162, 139, 124, 120, 189, 148, 69, 153 }, }; -static const uint8_t rgb_to_yuv_cb2_bt709_output[4][4] = { +static const uint8_mat4_t rgb_to_yuv_cb2_bt709_output = { { 100, 154, 142, 96 }, { 120, 130, 134, 153 }, { 112, 144, 147, 71 }, { 137, 153, 117, 140 }, }; -static const uint8_t rgb_to_yuv_cr2_bt709_output[4][4] = { +static const uint8_mat4_t rgb_to_yuv_cr2_bt709_output = { { 169, 154, 154, 118 }, { 140, 124, 118, 129 }, { 144, 133, 137, 113 }, { 146, 131, 135, 127 }, }; +static const uint8_mat8_t rgb_to_yuv_y_bt601fr_output = { + { 67, 72, 99, 136, 119, 138, 83, 222 }, + { 90, 181, 116, 91, 100, 98, 180, 123 }, + { 146, 68, 214, 231, 95, 135, 57, 153 }, + { 145, 45, 198, 185, 161, 196, 75, 90 }, + { 119, 89, 127, 157, 28, 173, 192, 187 }, + { 144, 89, 110, 112, 135, 114, 165, 55 }, + { 113, 201, 99, 84, 104, 90, 130, 159 }, + { 134, 57, 37, 69, 132, 170, 118, 145 }, +}; + +static const uint8_mat8_t rgb_to_yuv_cb_bt601fr_output = { + { 114, 116, 214, 86, 136, 162, 92, 95 }, + { 95, 40, 185, 130, 165, 95, 115, 67 }, + { 53, 119, 144, 83, 149, 159, 191, 54 }, + { 117, 180, 134, 157, 133, 100, 192, 184 }, + { 201, 97, 199, 98, 200, 130, 74, 31 }, + { 50, 80, 208, 74, 181, 80, 52, 105 }, + { 89, 49, 121, 215, 77, 204, 123, 152 }, + { 192, 213, 192, 93, 141, 35, 205, 86 }, +}; + +static const uint8_mat8_t rgb_to_yuv_cr_bt601fr_output = { + { 194, 101, 183, 207, 219, 99, 117, 136 }, + { 242, 166, 149, 80, 72, 234, 116, 108 }, + { 195, 79, 113, 117, 142, 167, 171, 174 }, + { 168, 126, 83, 177, 77, 77, 74, 86 }, + { 190, 98, 68, 75, 119, 185, 92, 170 }, + { 76, 221, 223, 161, 190, 51, 82, 116 }, + { 135, 158, 87, 189, 62, 132, 182, 104 }, + { 161, 133, 118, 121, 194, 156, 56, 159 }, +}; + +static const uint8_mat4_t rgb_to_yuv_cb2_bt601fr_output = { + { 91, 154, 140, 93 }, + { 117, 130, 135, 155 }, + { 107, 145, 148, 65 }, + { 136, 155, 114, 142 }, +}; + +static const uint8_mat4_t rgb_to_yuv_cr2_bt601fr_output = { + { 176, 155, 156, 119 }, + { 142, 122, 116, 126 }, + { 146, 132, 136, 115 }, + { 147, 129, 136, 125 }, +}; + +static const uint8_mat8_t rgb_to_yuv_y_bt709fr_output = { + { 55, 79, 77, 124, 99, 140, 90, 224 }, + { 69, 183, 105, 101, 108, 80, 184, 135 }, + { 140, 79, 215, 239, 89, 123, 40, 152 }, + { 138, 39, 206, 171, 171, 210, 79, 92 }, + { 97, 98, 131, 172, 21, 160, 206, 189 }, + { 164, 75, 80, 111, 115, 136, 183, 60 }, + { 116, 204, 108, 60, 124, 80, 119, 162 }, + { 119, 46, 31, 75, 116, 175, 124, 143 }, +}; + +static const uint8_mat8_t rgb_to_yuv_cb_bt709fr_output = { + { 122, 112, 222, 94, 147, 160, 90, 96 }, + { 107, 43, 189, 125, 160, 106, 114, 64 }, + { 60, 113, 143, 81, 151, 164, 197, 58 }, + { 121, 180, 129, 163, 127, 94, 187, 180 }, + { 209, 93, 193, 91, 201, 137, 69, 34 }, + { 42, 90, 220, 77, 189, 71, 45, 104 }, + { 90, 51, 116, 223, 68, 206, 129, 150 }, + { 197, 215, 192, 92, 149, 37, 198, 89 }, +}; + +static const uint8_mat8_t rgb_to_yuv_cr_bt709fr_output = { + { 195, 100, 191, 206, 222, 101, 114, 133 }, + { 243, 161, 154, 79, 73, 234, 115, 103 }, + { 191, 77, 114, 113, 144, 170, 177, 170 }, + { 168, 130, 83, 180, 76, 73, 78, 89 }, + { 197, 95, 72, 71, 124, 186, 87, 164 }, + { 69, 220, 232, 158, 195, 45, 75, 115 }, + { 132, 153, 86, 197, 57, 138, 183, 105 }, + { 167, 140, 123, 118, 197, 150, 60, 156 }, +}; + +static const uint8_mat4_t rgb_to_yuv_cb2_bt709fr_output = { + { 96, 157, 143, 91 }, + { 119, 129, 134, 156 }, + { 109, 145, 149, 63 }, + { 138, 156, 115, 142 }, +}; + +static const uint8_mat4_t rgb_to_yuv_cr2_bt709fr_output = { + { 175, 157, 157, 116 }, + { 142, 122, 116, 128 }, + { 145, 133, 138, 110 }, + { 148, 131, 135, 126 }, +}; + #define MAX_NUMBER_OF_PLANES 3 static const size_t num_log2_den[][2] = { @@ -376,22 +473,66 @@ static const size_t num_log2_den_per_plane[][3 * MAX_NUMBER_OF_PLANES] = { * magenta (255, 0, 255): 78, 214, 230 * cyan ( 0, 255, 255): 188, 154, 16 * white (255, 255, 255): 235, 128, 128 + * + * Color table (bt601 full range): + * r g b + * black ( 0, 0, 0): 0, 128, 128 + * red (255, 0, 0): 76, 84, 255 + * green ( 0, 255, 0): 149, 43, 21 + * yellow (255, 255, 0): 225, 0, 148 + * blue ( 0, 0, 255): 29, 255, 107 + * magenta (255, 0, 255): 105, 212, 234 + * cyan ( 0, 255, 255): 178, 171, 0 + * white (255, 255, 255): 255, 128, 128 + * + * Color table (bt709 full range): + * r g b + * black ( 0, 0, 0): 0, 128, 128 + * red (255, 0, 0): 54, 98, 255 + * green ( 0, 255, 0): 182, 29, 12 + * yellow (255, 255, 0): 237, 0, 139 + * blue ( 0, 0, 255): 18, 255, 116 + * magenta (255, 0, 255): 73, 226, 243 + * cyan ( 0, 255, 255): 201, 157, 0 + * white (255, 255, 255): 255, 128, 128 */ -static const uint8_t y_to_rgb_input[2][8] = { +static const uint8_t y_to_rgb_input[4][8] = { { 16, 82, 145, 210, 41, 107, 169, 235 }, - { 16, 63, 173, 219, 32, 78, 188, 235 } + { 16, 63, 173, 219, 32, 78, 188, 235 }, + { 0, 76, 149, 225, 29, 105, 178, 255 }, + { 0, 54, 182, 237, 18, 73, 201, 255 }, }; -static const uint8_t cb_to_rgb_input[2][8] = { +static const uint8_t cb_to_rgb_input[4][8] = { { 128, 90, 54, 16, 240, 202, 166, 128 }, - { 128, 102, 42, 16, 240, 214, 154, 128 } + { 128, 102, 42, 16, 240, 214, 154, 128 }, + { 128, 84, 43, 0, 255, 212, 171, 128 }, + { 128, 98, 29, 0, 255, 226, 157, 128 }, }; -static const uint8_t cr_to_rgb_input[2][8] = { +static const uint8_t cr_to_rgb_input[4][8] = { { 128, 240, 34, 146, 110, 222, 16, 128 }, - { 128, 240, 26, 138, 118, 230, 16, 128 } + { 128, 240, 26, 138, 118, 230, 16, 128 }, + { 128, 255, 21, 148, 107, 234, 0, 128 }, + { 128, 255, 12, 139, 116, 243, 0, 128 }, }; +static int32_t +is_valid_format(const DcpImageFormat *format, + uint32_t width, + uint32_t height) +{ + if (format->pixel_format == DCP_PIXEL_FORMAT_I444) { + return format->num_planes == 3; + } else if (format->pixel_format == DCP_PIXEL_FORMAT_I422 || format->pixel_format == DCP_PIXEL_FORMAT_I420) { + return format->num_planes == 3 && (width & 1) == 0 && (height & 1) == 0; + } else if (format->pixel_format == DCP_PIXEL_FORMAT_NV12) { + return (format->num_planes >= 1 && format->num_planes <= 2) && (width & 1) == 0 && (height & 1) == 0; + } else { + return format->num_planes == 1; + } +} + static void init(void) { @@ -551,11 +692,16 @@ convert_image_rgb_to_yuv_size_mode_stride(uint32_t num_planes, output = test_output; /* Check all luma samples are correct */ + const uint8_mat8_t *expected_y = (color_space == DCP_COLOR_SPACE_BT601 ? &rgb_to_yuv_y_bt601_output: + color_space == DCP_COLOR_SPACE_BT709 ? &rgb_to_yuv_y_bt709_output: + color_space == DCP_COLOR_SPACE_BT601FR ? &rgb_to_yuv_y_bt601fr_output: + &rgb_to_yuv_y_bt709fr_output); + for (y = 0; y < height; y++) { size_t x; for (x = 0; x < width; x++, count++, output++) { - TEST_ASSERT_EQ(*output, (color_space == DCP_COLOR_SPACE_BT601 ? rgb_to_yuv_y_bt601_output : rgb_to_yuv_y_bt709_output)[y][x]); + TEST_ASSERT_EQ(*output, (*expected_y)[y][x]); } for (x = 0; x < luma_fill_bytes; x++, count++, output++) { @@ -564,11 +710,20 @@ convert_image_rgb_to_yuv_size_mode_stride(uint32_t num_planes, } if (dst_pixel_format == DCP_PIXEL_FORMAT_I444) { + const uint8_mat8_t *expected_cb = (color_space == DCP_COLOR_SPACE_BT601 ? &rgb_to_yuv_cb_bt601_output: + color_space == DCP_COLOR_SPACE_BT709 ? &rgb_to_yuv_cb_bt709_output: + color_space == DCP_COLOR_SPACE_BT601FR ? &rgb_to_yuv_cb_bt601fr_output: + &rgb_to_yuv_cb_bt709fr_output); + const uint8_mat8_t *expected_cr = (color_space == DCP_COLOR_SPACE_BT601 ? &rgb_to_yuv_cr_bt601_output: + color_space == DCP_COLOR_SPACE_BT709 ? &rgb_to_yuv_cr_bt709_output: + color_space == DCP_COLOR_SPACE_BT601FR ? &rgb_to_yuv_cr_bt601fr_output: + &rgb_to_yuv_cr_bt709fr_output); + for (y = 0; y < height; y++) { size_t x; for (x = 0; x < width; x++, count++, output++) { - TEST_ASSERT_EQ(*output, (color_space == DCP_COLOR_SPACE_BT601 ? rgb_to_yuv_cb_bt601_output : rgb_to_yuv_cb_bt709_output)[y][x]); + TEST_ASSERT_EQ(*output, (*expected_cb)[y][x]); } for (x = 0; x < u_chroma_fill_bytes; x++, count++, output++) { @@ -580,7 +735,7 @@ convert_image_rgb_to_yuv_size_mode_stride(uint32_t num_planes, size_t x; for (x = 0; x < width; x++, count++, output++) { - TEST_ASSERT_EQ(*output, (color_space == DCP_COLOR_SPACE_BT601 ? rgb_to_yuv_cr_bt601_output : rgb_to_yuv_cr_bt709_output)[y][x]); + TEST_ASSERT_EQ(*output, (*expected_cr)[y][x]); } for (x = 0; x < v_chroma_fill_bytes; x++, count++, output++) { @@ -588,12 +743,21 @@ convert_image_rgb_to_yuv_size_mode_stride(uint32_t num_planes, } } } else if (dst_pixel_format == DCP_PIXEL_FORMAT_NV12) { + const uint8_mat4_t *expected_cb = (color_space == DCP_COLOR_SPACE_BT601 ? &rgb_to_yuv_cb2_bt601_output: + color_space == DCP_COLOR_SPACE_BT709 ? &rgb_to_yuv_cb2_bt709_output: + color_space == DCP_COLOR_SPACE_BT601FR ? &rgb_to_yuv_cb2_bt601fr_output: + &rgb_to_yuv_cb2_bt709fr_output); + const uint8_mat4_t *expected_cr = (color_space == DCP_COLOR_SPACE_BT601 ? &rgb_to_yuv_cr2_bt601_output: + color_space == DCP_COLOR_SPACE_BT709 ? &rgb_to_yuv_cr2_bt709_output: + color_space == DCP_COLOR_SPACE_BT601FR ? &rgb_to_yuv_cr2_bt601fr_output: + &rgb_to_yuv_cr2_bt709fr_output); + for (y = 0; y < chroma_height; y++) { size_t x; for (x = 0; x < width / 2; x++, count += 2, output += 2) { - TEST_ASSERT_EQ(*output, (color_space == DCP_COLOR_SPACE_BT601 ? rgb_to_yuv_cb2_bt601_output : rgb_to_yuv_cb2_bt709_output)[y][x]); - TEST_ASSERT_EQ(*(output + 1), (color_space == DCP_COLOR_SPACE_BT601 ? rgb_to_yuv_cr2_bt601_output : rgb_to_yuv_cr2_bt709_output)[y][x]); + TEST_ASSERT_EQ(*output, (*expected_cb)[y][x]); + TEST_ASSERT_EQ(*(output + 1), (*expected_cr)[y][x]); } for (x = 0; x < u_chroma_fill_bytes; x++, count++, output++) { @@ -601,11 +765,20 @@ convert_image_rgb_to_yuv_size_mode_stride(uint32_t num_planes, } } } else { + const uint8_mat4_t *expected_cb = (color_space == DCP_COLOR_SPACE_BT601 ? &rgb_to_yuv_cb2_bt601_output: + color_space == DCP_COLOR_SPACE_BT709 ? &rgb_to_yuv_cb2_bt709_output: + color_space == DCP_COLOR_SPACE_BT601FR ? &rgb_to_yuv_cb2_bt601fr_output: + &rgb_to_yuv_cb2_bt709fr_output); + const uint8_mat4_t *expected_cr = (color_space == DCP_COLOR_SPACE_BT601 ? &rgb_to_yuv_cr2_bt601_output: + color_space == DCP_COLOR_SPACE_BT709 ? &rgb_to_yuv_cr2_bt709_output: + color_space == DCP_COLOR_SPACE_BT601FR ? &rgb_to_yuv_cr2_bt601fr_output: + &rgb_to_yuv_cr2_bt709fr_output); + for (y = 0; y < chroma_height; y++) { size_t x; for (x = 0; x < width / 2; x++, count++, output++) { - TEST_ASSERT_EQ(*output, (color_space == DCP_COLOR_SPACE_BT601 ? rgb_to_yuv_cb2_bt601_output : rgb_to_yuv_cb2_bt709_output)[y][x]); + TEST_ASSERT_EQ(*output, (*expected_cb)[y][x]); } for (x = 0; x < u_chroma_fill_bytes; x++, count++, output++) { @@ -617,7 +790,7 @@ convert_image_rgb_to_yuv_size_mode_stride(uint32_t num_planes, size_t x; for (x = 0; x < width / 2; x++, count++, output++) { - TEST_ASSERT_EQ(*output, (color_space == DCP_COLOR_SPACE_BT601 ? rgb_to_yuv_cr2_bt601_output : rgb_to_yuv_cr2_bt709_output)[y][x]); + TEST_ASSERT_EQ(*output, (*expected_cr)[y][x]); } for (x = 0; x < v_chroma_fill_bytes; x++, count++, output++) { @@ -723,7 +896,7 @@ convert_image_rgb_to_yuv_size(uint32_t num_planes, TEST_BEGIN_GROUP("%ux%u", width, height); - for (color_space = DCP_COLOR_SPACE_BT601; color_space <= DCP_COLOR_SPACE_BT709; color_space++) { + for (color_space = DCP_COLOR_SPACE_BT601; color_space <= DCP_COLOR_SPACE_BT709FR; color_space++) { size_t i; for (i = 0; i < supported_pixel_formats_count; i++) { @@ -1086,7 +1259,7 @@ convert_image_yuv_to_rgb_size(DcpPixelFormat format, TEST_BEGIN_GROUP("%ux%u", width, height); - for (color_space = DCP_COLOR_SPACE_BT601; color_space <= DCP_COLOR_SPACE_BT709; color_space++) { + for (color_space = DCP_COLOR_SPACE_BT601; color_space <= DCP_COLOR_SPACE_BT709FR; color_space++) { convert_image_yuv_to_rgb_size_format(format, width, height, color_space); } @@ -1144,16 +1317,18 @@ unit_convert_image_rgb_to_yuv_errors(void) for (src_pixel_format = 0; src_pixel_format <= DCP_PIXEL_FORMAT_NV12 + 1; src_pixel_format++) { int32_t src_color_space; - for (src_color_space = 0; src_color_space <= DCP_COLOR_SPACE_BT709 + 1; src_color_space++) { + for (src_color_space = 0; src_color_space <= DCP_COLOR_SPACE_BT709FR + 1; src_color_space++) { int32_t dst_color_space; - for (dst_color_space = 0; dst_color_space <= DCP_COLOR_SPACE_BT709 + 1; dst_color_space++) { + for (dst_color_space = 0; dst_color_space <= DCP_COLOR_SPACE_BT709FR + 1; dst_color_space++) { int32_t corrupt; for (corrupt = 0; corrupt < 4; corrupt++) { uint8_t *src_buffer; size_t dst_strides[2]; uint8_t *dst_buffers[2]; + int32_t src_pf_rgb; + int32_t src_cs_rgb; DcpImageFormat src_format = { src_pixel_format, @@ -1203,22 +1378,21 @@ unit_convert_image_rgb_to_yuv_errors(void) TEST_ASSERT(DCP_RESULT_ERR, DCP_ERROR_KIND_INVALID_VALUE); + src_pf_rgb = src_pixel_format < DCP_PIXEL_FORMAT_I444; + src_cs_rgb = src_color_space == DCP_COLOR_SPACE_LRGB; expected = dcp_status(); SET_EXPECTED(src_pixel_format > DCP_PIXEL_FORMAT_NV12, DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED(src_color_space > DCP_COLOR_SPACE_BT709, DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED(dst_color_space > DCP_COLOR_SPACE_BT709, DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(src_color_space > DCP_COLOR_SPACE_BT709FR, DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(dst_color_space > DCP_COLOR_SPACE_BT709FR, DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED((width & 1) != 0, DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED((height & 1) != 0, DCP_ERROR_KIND_INVALID_VALUE); - - SET_EXPECTED((src_pixel_format >= DCP_PIXEL_FORMAT_I444) && (src_color_space <= DCP_COLOR_SPACE_LRGB), DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED((src_pixel_format < DCP_PIXEL_FORMAT_I444) && (src_color_space > DCP_COLOR_SPACE_LRGB), DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(!src_pf_rgb && src_cs_rgb, DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(src_pf_rgb && !src_cs_rgb, DCP_ERROR_KIND_INVALID_VALUE); SET_EXPECTED(dst_color_space <= DCP_COLOR_SPACE_LRGB, DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED(num_planes < 1, DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED(num_planes > 2, DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(is_valid_format(&src_format, width, height) == 0, DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(is_valid_format(&dst_format, width, height) == 0, DCP_ERROR_KIND_INVALID_VALUE); SET_EXPECTED(corrupt != 0, DCP_ERROR_KIND_INVALID_VALUE); @@ -1275,16 +1449,18 @@ unit_convert_image_yuv_to_rgb_errors(void) for (dst_pixel_format = 0; dst_pixel_format <= DCP_PIXEL_FORMAT_NV12 + 1; dst_pixel_format++) { int32_t dst_color_space; - for (dst_color_space = 0; dst_color_space <= DCP_COLOR_SPACE_BT709 + 1; dst_color_space++) { + for (dst_color_space = 0; dst_color_space <= DCP_COLOR_SPACE_BT709FR + 1; dst_color_space++) { int32_t src_color_space; - for (src_color_space = 0; src_color_space <= DCP_COLOR_SPACE_BT709 + 1; src_color_space++) { + for (src_color_space = 0; src_color_space <= DCP_COLOR_SPACE_BT709FR + 1; src_color_space++) { int32_t corrupt; for (corrupt = 0; corrupt < 4; corrupt++) { size_t src_strides[2]; uint8_t *src_buffers[2]; uint8_t *dst_buffer; + int32_t dst_pf_rgb; + int32_t dst_cs_rgb; DcpImageFormat src_format = { DCP_PIXEL_FORMAT_NV12, @@ -1334,22 +1510,21 @@ unit_convert_image_yuv_to_rgb_errors(void) TEST_ASSERT(DCP_RESULT_ERR, DCP_ERROR_KIND_INVALID_VALUE); + dst_pf_rgb = dst_pixel_format < DCP_PIXEL_FORMAT_I444; + dst_cs_rgb = dst_color_space == DCP_COLOR_SPACE_LRGB; expected = dcp_status(); - SET_EXPECTED(src_color_space > DCP_COLOR_SPACE_BT709, DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(src_color_space > DCP_COLOR_SPACE_BT709FR, DCP_ERROR_KIND_INVALID_VALUE); SET_EXPECTED(dst_pixel_format > DCP_PIXEL_FORMAT_NV12, DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED(dst_color_space > DCP_COLOR_SPACE_BT709, DCP_ERROR_KIND_INVALID_VALUE); - - SET_EXPECTED((width & 1) != 0, DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED((height & 1) != 0, DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(dst_color_space > DCP_COLOR_SPACE_BT709FR, DCP_ERROR_KIND_INVALID_VALUE); SET_EXPECTED(src_color_space <= DCP_COLOR_SPACE_LRGB, DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED((dst_pixel_format >= DCP_PIXEL_FORMAT_I444) && (dst_color_space <= DCP_COLOR_SPACE_LRGB), DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED((dst_pixel_format < DCP_PIXEL_FORMAT_I444) && (dst_color_space > DCP_COLOR_SPACE_LRGB), DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(is_valid_format(&src_format, width, height) == 0, DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(is_valid_format(&dst_format, width, height) == 0, DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED(num_planes < 1, DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED(num_planes > 2, DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(!dst_pf_rgb && dst_cs_rgb, DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(dst_pf_rgb && !dst_cs_rgb, DCP_ERROR_KIND_INVALID_VALUE); SET_EXPECTED(corrupt != 0, DCP_ERROR_KIND_INVALID_VALUE); @@ -1410,7 +1585,7 @@ unit_get_buffers_plane(int32_t num_planes) expected = dcp_status(); SET_EXPECTED(!is_pf_valid, DCP_ERROR_KIND_INVALID_VALUE); SET_EXPECTED(pf >= DCP_PIXEL_FORMAT_I422, DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED(num_planes != 1 && num_planes != max_number_of_planes, DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(is_valid_format(&format, valid_width, valid_height) == 0, DCP_ERROR_KIND_INVALID_VALUE); status.result = dcp_get_buffers_size(1, valid_height, &format, NULL, buffers_size, &status.error); TEST_ASSERT(expected.result, expected.error); @@ -1418,14 +1593,14 @@ unit_get_buffers_plane(int32_t num_planes) expected = dcp_status(); SET_EXPECTED(!is_pf_valid, DCP_ERROR_KIND_INVALID_VALUE); SET_EXPECTED(pf >= DCP_PIXEL_FORMAT_I420, DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED(num_planes != 1 && num_planes != max_number_of_planes, DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(is_valid_format(&format, valid_width, valid_height) == 0, DCP_ERROR_KIND_INVALID_VALUE); status.result = dcp_get_buffers_size(valid_width, 1, &format, NULL, buffers_size, &status.error); TEST_ASSERT(expected.result, expected.error); /* Test size is valid */ expected = dcp_status(); SET_EXPECTED(!is_pf_valid, DCP_ERROR_KIND_INVALID_VALUE); - SET_EXPECTED(num_planes != 1 && num_planes != max_number_of_planes, DCP_ERROR_KIND_INVALID_VALUE); + SET_EXPECTED(is_valid_format(&format, valid_width, valid_height) == 0, DCP_ERROR_KIND_INVALID_VALUE); status.result = dcp_get_buffers_size(valid_width, valid_height, &format, NULL, buffers_size, &status.error); TEST_ASSERT(expected.result, expected.error); if (expected.result == DCP_RESULT_OK) { diff --git a/genweights.py b/genweights.py new file mode 100644 index 0000000..3291e3e --- /dev/null +++ b/genweights.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 + +# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: MIT-0 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from fractions import Fraction as frac +import sys + +FIX16 = 16 +FIX14 = (FIX16 + 2) +FIX16_HALF = (1 << (FIX16 - 1)) +FIX14_HALF = (1 << (FIX16 + 1)) +FIX_8_14 = 14 +FIX_8_14_HALF = (1 << (FIX_8_14 - 1)) + +FIX16_MULT = (1 << FIX16) +FIX8_14_MULT = (1 << FIX_8_14) + +if __name__ == '__main__': + if len(sys.argv) < 3: + print('usage: python genweights.py [601|709] [0|1]') + exit(0) + + model = int(sys.argv[1]) + full_range = int(sys.argv[2]) == 1 + + if model == 601: + kr = 0.299 + kg = 0.587 + kb = 0.114 + elif model == 709: + kr = 0.2126 + kg = 0.7152 + kb = 0.0722 + else: + print('Invalid model %s' % sys.argv[1]) + exit(0) + + print('Model: %d' % model) + print('Full range: %d' % full_range) + print('') + + if full_range: + Y_MIN = 0 + Y_MAX = 255 + C_MIN = 0 + C_MAX = 255 + SUFFIX = "FR" + else: + Y_MIN = 16 + Y_MAX = 235 + C_MIN = 16 + C_MAX = 240 + SUFFIX = "" + + Y_RANGE = Y_MAX - Y_MIN + C_HALF = (C_MAX + C_MIN) >> 1 + C_RANGE = C_MAX - C_MIN + FULL_RANGE = 255 + Y_SCALE = (Y_RANGE / FULL_RANGE) + C_SCALE = (C_RANGE / FULL_RANGE) + + if full_range: + ar = kr + ag = kg + ab = kb + br = (-kr / (2.0 * (1.0 - kb))) + bg = (-kg / (2.0 * (1.0 - kb))) + bb = 0.5 + cr = 0.5 + cg = (-kg / (2.0 * (1.0 - kr))) + cb = (-kb / (2.0 * (1.0 - kr))) + else: + ar = Y_SCALE * kr + ag = Y_SCALE * kg + ab = Y_SCALE * kb + br = C_SCALE * (-kr / (2.0 * (1.0 - kb))) + bg = C_SCALE * (-kg / (2.0 * (1.0 - kb))) + bb = C_SCALE * 0.5 + cr = C_SCALE * 0.5 + cg = C_SCALE * (-kg / (2.0 * (1.0 - kr))) + cb = C_SCALE * (-kb / (2.0 * (1.0 - kr))) + + ikb = 1.0 - kb + ikr = 1.0 - kr + y_scale = 1 / Y_SCALE + c_scale = 2 * FULL_RANGE + + inverse = ( + (y_scale, 0, c_scale * ikr / C_RANGE, ), + (y_scale, -c_scale * ikb * kb / (C_RANGE * kg), - + c_scale * ikr * kr / (C_RANGE * kg), ), + (y_scale, c_scale * ikb / C_RANGE, 0, ) + ) + offset = ( + -((8.0 / 7.0) * ikr + (Y_MIN / Y_RANGE)), + ( + (Y_MIN / Y_RANGE) * (kr - ikb) + + (8.0 / 7.0) * (ikr * kr + ikb * kb) + ) / kg, + -((8.0 / 7.0) * ikb + (Y_MIN / Y_RANGE)) + ) + + print('[ Y\'] [ %6.3f, %6.3f %6.3f] [ R ] [%6d]' % (ar, ag, ab, Y_MIN)) + print('[Cb\'] = [ %6.3f, %6.3f %6.3f] [ G ] + [%6d]' % + (br, bg, bb, C_HALF)) + print('[Cr\'] [ %6.3f, %6.3f %6.3f] [ B ] [%6d]' % + (cr, cg, cb, C_HALF)) + print('') + + print('[ R ] [ %6.3f, %6.3f %6.3f] [ Y\'] [%6.3f]' % + (inverse[0][0], inverse[0][1], inverse[0][2], offset[0])) + print('[ G ] = [ %6.3f, %6.3f %6.3f] [Cb\'] + [%6.3f]' % + (inverse[1][0], inverse[1][1], inverse[1][2], offset[1])) + print('[ B ] [ %6.3f, %6.3f %6.3f] [Cr\'] [%6.3f]' % + (inverse[2][0], inverse[2][1], inverse[2][2], offset[2])) + print('') + + xr = int(FIX16_MULT * ar + 0.5) + xg = int(FIX16_MULT * ag + 0.5) + xb = int(FIX16_MULT * ab + 0.5) + yr = int(FIX16_MULT * br + 0.5) + yg = int(FIX16_MULT * bg + 0.5) + yb = int(FIX16_MULT * bb + 0.5) + zr = int(FIX16_MULT * cr + 0.5) + zg = int(FIX16_MULT * cg + 0.5) + zb = int(FIX16_MULT * cb + 0.5) + + rz = 2.0 * ikr / C_SCALE + gy = (2.0 * ikb * kb) / (C_SCALE * kg) + gz = (2.0 * ikr * kr) / (C_SCALE * kg) + by = 2.0 * ikb / C_SCALE + + s = int(FIX8_14_MULT * y_scale + 0.5) + rz = int(FIX8_14_MULT * rz + 0.5) + gy = int(FIX8_14_MULT * gy + 0.5) + gz = int(FIX8_14_MULT * gz + 0.5) + by = int(FIX8_14_MULT * by + 0.5) + + rw = rz * C_HALF + s * Y_MIN - FIX_8_14_HALF + gw = (gy * C_HALF) + (gz * C_HALF) - (s * Y_MIN) + FIX_8_14_HALF + bw = s * Y_MIN + by * C_HALF - FIX_8_14_HALF + + print('// Coefficient table for %s%s' % + (model, " (full range)" if full_range else "")) + print('pub const XR_%d%s: i32 = %d;' % (model, SUFFIX, xr)) + print('pub const XG_%d%s: i32 = %d;' % (model, SUFFIX, xg)) + print('pub const XB_%d%s: i32 = %d;' % (model, SUFFIX, xb)) + print('pub const YR_%d%s: i32 = %d;' % (model, SUFFIX, yr)) + print('pub const YG_%d%s: i32 = %d;' % (model, SUFFIX, yg)) + print('pub const YB_%d%s: i32 = %d;' % (model, SUFFIX, yb)) + print('pub const ZR_%d%s: i32 = %d;' % (model, SUFFIX, zr)) + print('pub const ZG_%d%s: i32 = %d;' % (model, SUFFIX, zg)) + print('pub const ZB_%d%s: i32 = %d;' % (model, SUFFIX, zb)) + print('') + + print('pub const XXYM_%d%s: i32 = %d;' % (model, SUFFIX, s)) + print('pub const RCRM_%d%s: i32 = %d;' % (model, SUFFIX, rz)) + print('pub const GCRM_%d%s: i32 = %d;' % (model, SUFFIX, gz)) + print('pub const GCBM_%d%s: i32 = %d;' % (model, SUFFIX, gy)) + print('pub const BCBM_%d%s: i32 = %d;' % (model, SUFFIX, by)) + print('pub const RN_%d%s: i32 = %d;' % (model, SUFFIX, rw >> 8)) + print('pub const GP_%d%s: i32 = %d;' % (model, SUFFIX, gw >> 8)) + print('pub const BN_%d%s: i32 = %d;' % (model, SUFFIX, bw >> 8)) + print('') diff --git a/include/dcv_color_primitives.h b/include/dcv_color_primitives.h index d7121ff..9ace8ad 100644 --- a/include/dcv_color_primitives.h +++ b/include/dcv_color_primitives.h @@ -36,6 +36,8 @@ * - YCbCr, ITU-R Recommendation BT.601 (standard video system) * - YCbCr, ITU-R Recommendation BT.709 (CSC systems) * + * Both standard range (0-235) and full range (0-255) are supported. + * * # Examples * * Initialize the library: @@ -328,6 +330,8 @@ typedef enum { * @DCP_COLOR_SPACE_LRGB: Gamma-corrected RGB * @DCP_COLOR_SPACE_BT601: YCbCr, ITU-R Recommendation BT.601 (standard video system) * @DCP_COLOR_SPACE_BT709: YCbCr, ITU-R Recommendation BT.709 (CSC systems) + * @DCP_COLOR_SPACE_BT601FR: YCbCr, BT.601 (full range) + * @DCP_COLOR_SPACE_BT709FR: YCbCr, BT.709 (full range) * * An enumeration of supported color models. * @@ -342,6 +346,8 @@ typedef enum { DCP_COLOR_SPACE_LRGB, DCP_COLOR_SPACE_BT601, DCP_COLOR_SPACE_BT709, + DCP_COLOR_SPACE_BT601FR, + DCP_COLOR_SPACE_BT709FR, } DcpColorSpace; /** @@ -366,10 +372,10 @@ typedef enum { * DCP_PIXEL_FORMAT_BGR | DCP_COLOR_SPACE_LRGB * DCP_PIXEL_FORMAT_RGBA | DCP_COLOR_SPACE_LRGB * DCP_PIXEL_FORMAT_RGB | DCP_COLOR_SPACE_LRGB - * DCP_PIXEL_FORMAT_I444 | DCP_COLOR_SPACE_BT601, DCP_COLOR_SPACE_BT709 - * DCP_PIXEL_FORMAT_I422 | DCP_COLOR_SPACE_BT601, DCP_COLOR_SPACE_BT709 - * DCP_PIXEL_FORMAT_I420 | DCP_COLOR_SPACE_BT601, DCP_COLOR_SPACE_BT709 - * DCP_PIXEL_FORMAT_NV12 | DCP_COLOR_SPACE_BT601, DCP_COLOR_SPACE_BT709 + * DCP_PIXEL_FORMAT_I444 | DCP_COLOR_SPACE_BT601(FR), DCP_COLOR_SPACE_BT709(FR) + * DCP_PIXEL_FORMAT_I422 | DCP_COLOR_SPACE_BT601(FR), DCP_COLOR_SPACE_BT709(FR) + * DCP_PIXEL_FORMAT_I420 | DCP_COLOR_SPACE_BT601(FR), DCP_COLOR_SPACE_BT709(FR) + * DCP_PIXEL_FORMAT_NV12 | DCP_COLOR_SPACE_BT601(FR), DCP_COLOR_SPACE_BT709(FR) * * Some pixel formats might impose additional restrictions on the accepted number of * planes and the image size: @@ -426,7 +432,7 @@ void dcp_initialize (void); * Returns a description of the algorithms that are best for the running cpu and * available instruction sets * - * Returns: a null-terminated string that contains the description, or %NULL if the library was not + * Returns: a null-terminated string that contains the description, or %NULL if the library was not * initialized before. String has to freed using function(dcp_unref_string) * * # Examples @@ -633,10 +639,18 @@ DcpResult dcp_get_buffers_size (uint32_t width, * * Source image pixel format | Supported destination image pixel formats * ----------------------------------|------------------------------------------ + * DCP_PIXEL_FORMAT_ARGB | DCP_PIXEL_FORMAT_I420 [1][algo-1] + * DCP_PIXEL_FORMAT_ARGB | DCP_PIXEL_FORMAT_I444 [1][algo-1] * DCP_PIXEL_FORMAT_ARGB | DCP_PIXEL_FORMAT_NV12 [1][algo-1] - * DCP_PIXEL_FORMAT_BGR | DCP_PIXEL_FORMAT_NV12 [1][algo-1] + * DCP_PIXEL_FORMAT_BGRA | DCP_PIXEL_FORMAT_I420 [1][algo-1] + * DCP_PIXEL_FORMAT_BGRA | DCP_PIXEL_FORMAT_I444 [1][algo-1] * DCP_PIXEL_FORMAT_BGRA | DCP_PIXEL_FORMAT_NV12 [1][algo-1] + * DCP_PIXEL_FORMAT_BGRA | DCP_PIXEL_FORMAT_RGB [4][algo-4] + * DCP_PIXEL_FORMAT_BGR | DCP_PIXEL_FORMAT_I420 [1][algo-1] + * DCP_PIXEL_FORMAT_BGR | DCP_PIXEL_FORMAT_I444 [1][algo-1] + * DCP_PIXEL_FORMAT_BGR | DCP_PIXEL_FORMAT_NV12 [1][algo-1] * DCP_PIXEL_FORMAT_I420 | DCP_PIXEL_FORMAT_BGRA [2][algo-2] + * DCP_PIXEL_FORMAT_I444 | DCP_PIXEL_FORMAT_BGRA [2][algo-2] * DCP_PIXEL_FORMAT_NV12 | DCP_PIXEL_FORMAT_BGRA [2][algo-2] * DCP_PIXEL_FORMAT_RGB | DCP_PIXEL_FORMAT_BGRA [3][algo-3] * @@ -671,6 +685,22 @@ DcpResult dcp_get_buffers_size (uint32_t width, * cr = 0.511 * r - 0.464 * g - 0.047 * b + 128 * ]| * + * If the destination image color space is Bt601FR, the following formula is applied: + * + * |[ + * y = 0.299 * r + 0.587 * g + 0.114 * b + * cb = -0.169 * r - 0.331 * g + 0.500 * b + 128 + * cr = 0.500 * r - 0.419 * g - 0.081 * b + 128 + * ]| + * + * If the destination image color space is Bt709FR, the following formula is applied: + * + * |[ + * y = 0.213 * r + 0.715 * g + 0.072 * b + * cb = -0.115 * r - 0.385 * g + 0.500 * b + 128 + * cr = 0.500 * r - 0.454 * g - 0.046 * b + 128 + * ]| + * * # Algorithm 2 # {#algo-2} * * Conversion from YCbCr model to linear RGB model, with 4:4:4 upsampling @@ -693,9 +723,29 @@ DcpResult dcp_get_buffers_size (uint32_t width, * b = 1.164 * (y - 16) + 2.115 * (cb - 128) * ]| * + * If the source image color space is Bt601FR, the following formula is applied: + * + * |[ + * r = y + 1.402 * (cr - 128) + * g = y - 0.714 * (cr - 128) - 0.344 * (cb - 128) + * b = y + 1.772 * (cb - 128) + * ]| + * + * If the source image color space is Bt709FR, the following formula is applied: + * + * |[ + * r = y + 1.575 * (cr - 128) + * g = y - 0.468 * (cr - 128) - 0.187 * (cb - 128) + * b = y + 1.856 * (cb - 128) + * ]| + * * # Algorithm 3 # {#algo-3} * * Conversion from RGB to BGRA + * + * # Algorithm 4 # {#algo-4} + * + * Conversion from BGRA to RGB */ DcpResult dcp_convert_image (uint32_t width, uint32_t height, diff --git a/src/color_space.rs b/src/color_space.rs index 195ae7e..6f55dee 100644 --- a/src/color_space.rs +++ b/src/color_space.rs @@ -30,4 +30,8 @@ pub enum ColorSpace { Bt601, /// YCbCr, ITU-R Recommendation BT.709 (CSC systems) Bt709, + /// YCbCr, BT.601 (full range) + Bt601FR, + /// YCbCr, BT.709 (full range) + Bt709FR, } diff --git a/src/convert_image/avx2.rs b/src/convert_image/avx2.rs index 96d9e86..1751ab2 100644 --- a/src/convert_image/avx2.rs +++ b/src/convert_image/avx2.rs @@ -22,28 +22,30 @@ use core::ptr::{read_unaligned as loadu, write_unaligned as storeu}; #[cfg(target_arch = "x86")] use core::arch::x86::{ - __m256i, _mm256_add_epi16, _mm256_add_epi32, _mm256_cmpeq_epi32, _mm256_extracti128_si256, - _mm256_madd_epi16, _mm256_mulhi_epu16, _mm256_or_si256, _mm256_packs_epi32, - _mm256_packus_epi16, _mm256_permute2x128_si256, _mm256_permute4x64_epi64, - _mm256_permutevar8x32_epi32, _mm256_set1_epi16, _mm256_set1_epi32, _mm256_set_epi32, - _mm256_set_epi64x, _mm256_set_m128i, _mm256_setr_epi32, _mm256_setr_epi8, _mm256_setzero_si256, - _mm256_shuffle_epi8, _mm256_slli_epi16, _mm256_slli_epi32, _mm256_srai_epi16, - _mm256_srai_epi32, _mm256_srli_epi16, _mm256_srli_epi32, _mm256_srli_si256, _mm256_sub_epi16, - _mm256_unpackhi_epi16, _mm256_unpackhi_epi8, _mm256_unpacklo_epi16, _mm256_unpacklo_epi32, - _mm256_unpacklo_epi64, _mm256_unpacklo_epi8, _mm_prefetch, _mm_setzero_si128, _MM_HINT_NTA, + __m256i, _mm256_add_epi16, _mm256_add_epi32, _mm256_and_si256, _mm256_cmpeq_epi32, + _mm256_extracti128_si256, _mm256_madd_epi16, _mm256_mulhi_epu16, _mm256_or_si256, + _mm256_packs_epi32, _mm256_packus_epi16, _mm256_permute2x128_si256, _mm256_permute4x64_epi64, + _mm256_permutevar8x32_epi32, _mm256_set1_epi16, _mm256_set1_epi32, _mm256_set1_epi64x, + _mm256_set_epi16, _mm256_set_epi32, _mm256_set_epi64x, _mm256_set_m128i, _mm256_setr_epi32, + _mm256_setr_epi8, _mm256_setzero_si256, _mm256_shuffle_epi8, _mm256_slli_epi16, + _mm256_slli_epi32, _mm256_srai_epi16, _mm256_srai_epi32, _mm256_srli_epi16, _mm256_srli_epi32, + _mm256_srli_si256, _mm256_sub_epi16, _mm256_unpackhi_epi16, _mm256_unpackhi_epi8, + _mm256_unpacklo_epi16, _mm256_unpacklo_epi32, _mm256_unpacklo_epi64, _mm256_unpacklo_epi8, + _mm_prefetch, _mm_setzero_si128, _MM_HINT_NTA, }; #[cfg(target_arch = "x86_64")] use core::arch::x86_64::{ - __m256i, _mm256_add_epi16, _mm256_add_epi32, _mm256_cmpeq_epi32, _mm256_extract_epi64, - _mm256_extracti128_si256, _mm256_madd_epi16, _mm256_mulhi_epu16, _mm256_or_si256, - _mm256_packs_epi32, _mm256_packus_epi16, _mm256_permute2x128_si256, _mm256_permute4x64_epi64, - _mm256_permutevar8x32_epi32, _mm256_set1_epi16, _mm256_set1_epi32, _mm256_set_epi32, - _mm256_set_epi64x, _mm256_set_m128i, _mm256_setr_epi32, _mm256_setr_epi8, _mm256_setzero_si256, - _mm256_shuffle_epi8, _mm256_slli_epi16, _mm256_slli_epi32, _mm256_srai_epi16, - _mm256_srai_epi32, _mm256_srli_epi16, _mm256_srli_epi32, _mm256_srli_si256, _mm256_sub_epi16, - _mm256_unpackhi_epi16, _mm256_unpackhi_epi8, _mm256_unpacklo_epi16, _mm256_unpacklo_epi32, - _mm256_unpacklo_epi64, _mm256_unpacklo_epi8, _mm_prefetch, _mm_setzero_si128, _MM_HINT_NTA, + __m256i, _mm256_add_epi16, _mm256_add_epi32, _mm256_and_si256, _mm256_cmpeq_epi32, + _mm256_extract_epi64, _mm256_extracti128_si256, _mm256_madd_epi16, _mm256_mulhi_epu16, + _mm256_or_si256, _mm256_packs_epi32, _mm256_packus_epi16, _mm256_permute2x128_si256, + _mm256_permute4x64_epi64, _mm256_permutevar8x32_epi32, _mm256_set1_epi16, _mm256_set1_epi32, + _mm256_set1_epi64x, _mm256_set_epi32, _mm256_set_epi64x, _mm256_set_m128i, _mm256_setr_epi32, + _mm256_setr_epi8, _mm256_setzero_si256, _mm256_shuffle_epi8, _mm256_slli_epi16, + _mm256_slli_epi32, _mm256_srai_epi16, _mm256_srai_epi32, _mm256_srli_epi16, _mm256_srli_epi32, + _mm256_srli_si256, _mm256_sub_epi16, _mm256_unpackhi_epi16, _mm256_unpackhi_epi8, + _mm256_unpacklo_epi16, _mm256_unpacklo_epi32, _mm256_unpacklo_epi64, _mm256_unpacklo_epi8, + _mm_prefetch, _mm_setzero_si128, _MM_HINT_NTA, }; const LANE_COUNT: usize = 32; @@ -79,7 +81,7 @@ macro_rules! align_dqword_2x96 { }; } -const FORWARD_WEIGHTS: [[i32; 6]; Colorimetry::Length as usize] = [ +const FORWARD_WEIGHTS: [[i32; 8]; Colorimetry::Length as usize] = [ [ i32x2_to_i32(XG_601 - SHORT_HALF, XR_601), i32x2_to_i32(SHORT_HALF, XB_601), @@ -87,6 +89,8 @@ const FORWARD_WEIGHTS: [[i32; 6]; Colorimetry::Length as usize] = [ i32x2_to_i32(YG_601, YR_601), i32x2_to_i32(0, ZB_601), i32x2_to_i32(0, YB_601), + Y_OFFSET, + 0, ], [ i32x2_to_i32(XG_709 - SHORT_HALF, XR_709), @@ -95,6 +99,28 @@ const FORWARD_WEIGHTS: [[i32; 6]; Colorimetry::Length as usize] = [ i32x2_to_i32(YG_709, YR_709), i32x2_to_i32(0, ZB_709), i32x2_to_i32(0, YB_709), + Y_OFFSET, + 0, + ], + [ + i32x2_to_i32(XG_601FR - SHORT_HALF, XR_601FR), + i32x2_to_i32(SHORT_HALF, XB_601FR), + i32x2_to_i32(ZG_601FR, ZR_601FR - SHORT_HALF), + i32x2_to_i32(YG_601FR, YR_601FR), + i32x2_to_i32(0, ZB_601FR), + i32x2_to_i32(0, YB_601FR - SHORT_HALF), + FIX16_HALF, + 1, + ], + [ + i32x2_to_i32(XG_709FR - SHORT_HALF, XR_709FR), + i32x2_to_i32(SHORT_HALF, XB_709FR), + i32x2_to_i32(ZG_709FR, ZR_709FR - SHORT_HALF), + i32x2_to_i32(YG_709FR, YR_709FR), + i32x2_to_i32(0, ZB_709FR), + i32x2_to_i32(0, YB_709FR - SHORT_HALF), + FIX16_HALF, + 1, ], ]; @@ -119,6 +145,26 @@ const BACKWARD_WEIGHTS: [[i16; 8]; Colorimetry::Length as usize] = [ i32_to_i16(GP_709), i32_to_i16(BN_709), ], + [ + i32_to_i16(XXYM_601FR), + i32_to_i16(RCRM_601FR), + i32_to_i16(GCRM_601FR), + i32_to_i16(GCBM_601FR), + i32_to_i16(BCBM_601FR), + i32_to_i16(RN_601FR), + i32_to_i16(GP_601FR), + i32_to_i16(BN_601FR), + ], + [ + i32_to_i16(XXYM_709FR), + i32_to_i16(RCRM_709FR), + i32_to_i16(GCRM_709FR), + i32_to_i16(GCBM_709FR), + i32_to_i16(BCBM_709FR), + i32_to_i16(RN_709FR), + i32_to_i16(GP_709FR), + i32_to_i16(BN_709FR), + ], ]; /// Convert fixed point to int (8-wide) @@ -294,6 +340,7 @@ unsafe fn lrgb_to_yuv_8x( sampler: Sampler, y_weigths: &[__m256i; 3], uv_weights: &[__m256i; 3], + full_range: bool, ) { let (rg0, bg0) = unpack_ui8x3_i16x2_8x(rgb0, sampler); pack_i32_8x( @@ -309,10 +356,21 @@ unsafe fn lrgb_to_yuv_8x( let srg = sum_i16x2_neighborhood_4x(rg0, rg1); let sbg = sum_i16x2_neighborhood_4x(bg0, bg1); - pack_i32_8x( - uv, - fix_to_i32_8x!(affine_transform(srg, sbg, uv_weights), FIX18), - ); + let mut t = affine_transform(srg, sbg, uv_weights); + if full_range { + t = _mm256_add_epi32( + t, + _mm256_slli_epi32( + _mm256_or_si256( + _mm256_and_si256(sbg, _mm256_set1_epi64x(0xFFFF_i64)), + _mm256_and_si256(srg, _mm256_set1_epi64x(0xFFFF_0000_0000_i64)), + ), + 14, + ), + ); + } + + pack_i32_8x(uv, fix_to_i32_8x!(t, FIX18)); } #[inline(always)] @@ -326,6 +384,7 @@ unsafe fn lrgb_to_i420_8x( sampler: Sampler, y_weigths: &[__m256i; 3], uv_weights: &[__m256i; 3], + full_range: bool, ) { let (rg0, bg0) = unpack_ui8x3_i16x2_8x(rgb0, sampler); pack_i32_8x( @@ -341,9 +400,22 @@ unsafe fn lrgb_to_i420_8x( let srg = sum_i16x2_neighborhood_4x(rg0, rg1); let sbg = sum_i16x2_neighborhood_4x(bg0, bg1); + let mut t = affine_transform(srg, sbg, uv_weights); + if full_range { + t = _mm256_add_epi32( + t, + _mm256_slli_epi32( + _mm256_or_si256( + _mm256_and_si256(sbg, _mm256_set1_epi64x(0xFFFF_i64)), + _mm256_and_si256(srg, _mm256_set1_epi64x(0xFFFF_0000_0000_i64)), + ), + 14, + ), + ); + } let shuff = _mm256_permutevar8x32_epi32( - fix_to_i32_8x!(affine_transform(srg, sbg, uv_weights), FIX18), + fix_to_i32_8x!(t, FIX18), _mm256_set_epi32(7, 5, 3, 1, 6, 4, 2, 0), ); @@ -373,6 +445,7 @@ unsafe fn lrgb_to_i444_8x( y_weights: &[__m256i; 3], u_weights: &[__m256i; 3], v_weights: &[__m256i; 3], + full_range: bool, ) { let (rg, bg) = unpack_ui8x3_i16x2_8x(rgb, sampler); pack_i32_8x( @@ -380,15 +453,15 @@ unsafe fn lrgb_to_i444_8x( fix_to_i32_8x!(affine_transform(rg, bg, y_weights), FIX16), ); - pack_i32_8x( - u, - fix_to_i32_8x!(affine_transform(rg, bg, u_weights), FIX16), - ); + let mut tu = affine_transform(rg, bg, u_weights); + let mut tv = affine_transform(rg, bg, v_weights); + if full_range { + tu = _mm256_add_epi32(tu, _mm256_srli_epi32(_mm256_slli_epi32(bg, 16), 2)); + tv = _mm256_add_epi32(tv, _mm256_srli_epi32(_mm256_slli_epi32(rg, 16), 2)); + } - pack_i32_8x( - v, - fix_to_i32_8x!(affine_transform(rg, bg, v_weights), FIX16), - ); + pack_i32_8x(u, fix_to_i32_8x!(tu, FIX16)); + pack_i32_8x(v, fix_to_i32_8x!(tv, FIX16)); } #[cfg(not(tarpaulin_include))] @@ -408,7 +481,7 @@ unsafe fn lrgb_to_yuv_avx2( dst_strides: (usize, usize), dst_buffers: &mut (&mut [u8], &mut [u8]), depth: usize, - weights: &[i32; 6], + weights: &[i32; 8], sampler: Sampler, ) { const DST_DEPTH: usize = LRGB_TO_YUV_WAVES; @@ -418,7 +491,7 @@ unsafe fn lrgb_to_yuv_avx2( let y_weigths = [ _mm256_set1_epi32(weights[0]), _mm256_set1_epi32(weights[1]), - _mm256_set1_epi32(Y_OFFSET), + _mm256_set1_epi32(weights[6]), ]; let uv_weights = [ @@ -430,7 +503,7 @@ unsafe fn lrgb_to_yuv_avx2( weights[4], weights[5], weights[4], weights[5], weights[4], weights[5], weights[4], weights[5], ), - _mm256_set1_epi32(C_OFFSET), + _mm256_set1_epi32(C_OFFSET - FIX18_HALF * weights[7]), ]; let src_group = src_buffer.as_ptr(); @@ -459,6 +532,7 @@ unsafe fn lrgb_to_yuv_avx2( sampler, &y_weigths, &uv_weights, + weights[7] == 1, ); } } @@ -476,6 +550,7 @@ unsafe fn lrgb_to_yuv_avx2( sampler, &y_weigths, &uv_weights, + weights[7] == 1, ); } @@ -489,6 +564,7 @@ unsafe fn lrgb_to_yuv_avx2( Sampler::BgrOverflow, &y_weigths, &uv_weights, + weights[7] == 1, ); } } @@ -503,7 +579,7 @@ unsafe fn lrgb_to_i420_avx2( dst_strides: (usize, usize, usize), dst_buffers: &mut (&mut [u8], &mut [u8], &mut [u8]), depth: usize, - weights: &[i32; 6], + weights: &[i32; 8], sampler: Sampler, ) { let (y_stride, u_stride, v_stride) = dst_strides; @@ -511,7 +587,7 @@ unsafe fn lrgb_to_i420_avx2( let y_weigths = [ _mm256_set1_epi32(weights[0]), _mm256_set1_epi32(weights[1]), - _mm256_set1_epi32(Y_OFFSET), + _mm256_set1_epi32(weights[6]), ]; let uv_weights = [ @@ -523,7 +599,7 @@ unsafe fn lrgb_to_i420_avx2( weights[4], weights[5], weights[4], weights[5], weights[4], weights[5], weights[4], weights[5], ), - _mm256_set1_epi32(C_OFFSET), + _mm256_set1_epi32(C_OFFSET - FIX18_HALF * weights[7]), ]; let src_group = src_buffer.as_ptr(); @@ -554,6 +630,7 @@ unsafe fn lrgb_to_i420_avx2( sampler, &y_weigths, &uv_weights, + weights[7] == 1, ); } } @@ -572,6 +649,7 @@ unsafe fn lrgb_to_i420_avx2( sampler, &y_weigths, &uv_weights, + weights[7] == 1, ); } @@ -586,6 +664,7 @@ unsafe fn lrgb_to_i420_avx2( Sampler::BgrOverflow, &y_weigths, &uv_weights, + weights[7] == 1, ); } } @@ -805,7 +884,7 @@ unsafe fn lrgb_to_i444_avx2( dst_strides: (usize, usize, usize), dst_buffers: &mut (&mut [u8], &mut [u8], &mut [u8]), depth: usize, - weights: &[i32; 6], + weights: &[i32; 8], sampler: Sampler, ) { let (y_stride, u_stride, v_stride) = dst_strides; @@ -813,19 +892,19 @@ unsafe fn lrgb_to_i444_avx2( let y_weights = [ _mm256_set1_epi32(weights[0]), _mm256_set1_epi32(weights[1]), - _mm256_set1_epi32(Y_OFFSET), + _mm256_set1_epi32(weights[6]), ]; let u_weights = [ _mm256_set1_epi32(weights[3]), _mm256_set1_epi32(weights[5]), - _mm256_set1_epi32(C_OFFSET16), + _mm256_set1_epi32(C_OFFSET16 - FIX16_HALF * weights[7]), ]; let v_weights = [ _mm256_set1_epi32(weights[2]), _mm256_set1_epi32(weights[4]), - _mm256_set1_epi32(C_OFFSET16), + _mm256_set1_epi32(C_OFFSET16 - FIX16_HALF * weights[7]), ]; let src_group = src_buffer.as_ptr(); @@ -855,6 +934,7 @@ unsafe fn lrgb_to_i444_avx2( &y_weights, &u_weights, &v_weights, + weights[7] == 1, ); } } @@ -872,6 +952,7 @@ unsafe fn lrgb_to_i444_avx2( &y_weights, &u_weights, &v_weights, + weights[7] == 1, ); } @@ -885,6 +966,7 @@ unsafe fn lrgb_to_i444_avx2( &y_weights, &u_weights, &v_weights, + weights[7] == 1, ); } } @@ -2511,3 +2593,551 @@ pub fn bgra_lrgb_rgb_lrgb( true } + +pub fn argb_lrgb_nv12_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_nv12_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_nv12_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt601FR as usize, + Sampler::Bgr, + ) +} + +pub fn nv12_bt601fr_bgra_lrgb( + width: u32, + height: u32, + last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + nv12_bgra_lrgb( + width, + height, + last_src_plane as usize, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt601FR as usize, + ) +} + +pub fn i420_bt601fr_bgra_lrgb( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + i420_bgra_lrgb( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt601FR as usize, + ) +} + +pub fn i444_bt601fr_bgra_lrgb( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + i444_bgra_lrgb( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt601FR as usize, + ) +} + +pub fn argb_lrgb_i420_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_i420_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_i420_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt601FR as usize, + Sampler::Bgr, + ) +} + +pub fn argb_lrgb_i444_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_i444_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_i444_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt601FR as usize, + Sampler::Bgr, + ) +} + +pub fn argb_lrgb_nv12_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_nv12_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_nv12_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt709FR as usize, + Sampler::Bgr, + ) +} + +pub fn nv12_bt709fr_bgra_lrgb( + width: u32, + height: u32, + last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + nv12_bgra_lrgb( + width, + height, + last_src_plane as usize, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt709FR as usize, + ) +} + +pub fn i420_bt709fr_bgra_lrgb( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + i420_bgra_lrgb( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt709FR as usize, + ) +} + +pub fn i444_bt709fr_bgra_lrgb( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + i444_bgra_lrgb( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt709FR as usize, + ) +} + +pub fn argb_lrgb_i420_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_i420_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_i420_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt709FR as usize, + Sampler::Bgr, + ) +} + +pub fn argb_lrgb_i444_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_i444_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_i444_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt709FR as usize, + Sampler::Bgr, + ) +} diff --git a/src/convert_image/common.rs b/src/convert_image/common.rs index 7d26341..2502129 100644 --- a/src/convert_image/common.rs +++ b/src/convert_image/common.rs @@ -71,7 +71,7 @@ pub const FIX6: i32 = 6; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub const SHORT_HALF: i32 = 16384; -// Cooefficient table for 601 +// Coefficient table for 601 pub const XR_601: i32 = 16829; pub const XG_601: i32 = 33039; pub const XB_601: i32 = 6416; @@ -91,7 +91,7 @@ pub const RN_601: i32 = 14234; pub const GP_601: i32 = 8709; pub const BN_601: i32 = 17685; -// Cooefficient table for 709 +// Coefficient table for 709 pub const XR_709: i32 = 11966; pub const XG_709: i32 = 40254; pub const XB_709: i32 = 4064; @@ -111,11 +111,51 @@ pub const RN_709: i32 = 15846; pub const GP_709: i32 = 4952; pub const BN_709: i32 = 18465; +// Coefficient table for 601 (full range) +pub const XR_601FR: i32 = 19595; +pub const XG_601FR: i32 = 38470; +pub const XB_601FR: i32 = 7471; +pub const YR_601FR: i32 = -11057; +pub const YG_601FR: i32 = -21709; +pub const YB_601FR: i32 = 32768; +pub const ZR_601FR: i32 = 32768; +pub const ZG_601FR: i32 = -27438; +pub const ZB_601FR: i32 = -5328; + +pub const XXYM_601FR: i32 = 16384; +pub const RCRM_601FR: i32 = 22970; +pub const GCRM_601FR: i32 = 11700; +pub const GCBM_601FR: i32 = 5638; +pub const BCBM_601FR: i32 = 29032; +pub const RN_601FR: i32 = 11363; +pub const GP_601FR: i32 = 8633; +pub const BN_601FR: i32 = 14370; + +// Coefficient table for 709 (full range) +pub const XR_709FR: i32 = 13933; +pub const XG_709FR: i32 = 46871; +pub const XB_709FR: i32 = 4732; +pub const YR_709FR: i32 = -7508; +pub const YG_709FR: i32 = -25258; +pub const YB_709FR: i32 = 32768; +pub const ZR_709FR: i32 = 32768; +pub const ZG_709FR: i32 = -29762; +pub const ZB_709FR: i32 = -3004; + +pub const XXYM_709FR: i32 = 16384; +pub const RCRM_709FR: i32 = 25802; +pub const GCRM_709FR: i32 = 7670; +pub const GCBM_709FR: i32 = 3069; +pub const BCBM_709FR: i32 = 30402; +pub const RN_709FR: i32 = 12768; +pub const GP_709FR: i32 = 5359; +pub const BN_709FR: i32 = 15050; + // Other defines -pub const Y_MIN: i32 = 16; -pub const C_HALF: i32 = 128; -pub const FIX16_Y_MIN: i32 = u8_to_fix(Y_MIN, FIX16); -pub const FIX16_C_HALF: i32 = u8_to_fix(C_HALF, FIX16); +const Y_MIN: i32 = 16; +const C_HALF: i32 = 128; +const FIX16_Y_MIN: i32 = u8_to_fix(Y_MIN, FIX16); +const FIX16_C_HALF: i32 = u8_to_fix(C_HALF, FIX16); pub const FIX18_C_HALF: i32 = u8_to_fix(C_HALF, FIX18); pub const Y_OFFSET: i32 = FIX16_Y_MIN + FIX16_HALF; pub const C_OFFSET: i32 = FIX18_C_HALF + FIX18_HALF; @@ -134,6 +174,8 @@ pub enum Sampler { pub enum Colorimetry { Bt601, Bt709, + Bt601FR, + Bt709FR, Length, } diff --git a/src/convert_image/sse2.rs b/src/convert_image/sse2.rs index bbdbc20..8deb3b3 100644 --- a/src/convert_image/sse2.rs +++ b/src/convert_image/sse2.rs @@ -65,7 +65,7 @@ macro_rules! xcgh_odd_even_words { }; } -const FORWARD_WEIGHTS: [[i32; 6]; Colorimetry::Length as usize] = [ +const FORWARD_WEIGHTS: [[i32; 8]; Colorimetry::Length as usize] = [ [ i32x2_to_i32(XG_601 - SHORT_HALF, XR_601), i32x2_to_i32(SHORT_HALF, XB_601), @@ -73,6 +73,8 @@ const FORWARD_WEIGHTS: [[i32; 6]; Colorimetry::Length as usize] = [ i32x2_to_i32(YG_601, YR_601), i32x2_to_i32(0, ZB_601), i32x2_to_i32(0, YB_601), + Y_OFFSET, + 0, ], [ i32x2_to_i32(XG_709 - SHORT_HALF, XR_709), @@ -81,6 +83,28 @@ const FORWARD_WEIGHTS: [[i32; 6]; Colorimetry::Length as usize] = [ i32x2_to_i32(YG_709, YR_709), i32x2_to_i32(0, ZB_709), i32x2_to_i32(0, YB_709), + Y_OFFSET, + 0, + ], + [ + i32x2_to_i32(XG_601FR - SHORT_HALF, XR_601FR), + i32x2_to_i32(SHORT_HALF, XB_601FR), + i32x2_to_i32(ZG_601FR, ZR_601FR - SHORT_HALF), + i32x2_to_i32(YG_601FR, YR_601FR), + i32x2_to_i32(0, ZB_601FR), + i32x2_to_i32(0, YB_601FR - SHORT_HALF), + FIX16_HALF, + 1, + ], + [ + i32x2_to_i32(XG_709FR - SHORT_HALF, XR_709FR), + i32x2_to_i32(SHORT_HALF, XB_709FR), + i32x2_to_i32(ZG_709FR, ZR_709FR - SHORT_HALF), + i32x2_to_i32(YG_709FR, YR_709FR), + i32x2_to_i32(0, ZB_709FR), + i32x2_to_i32(0, YB_709FR - SHORT_HALF), + FIX16_HALF, + 1, ], ]; @@ -105,6 +129,26 @@ const BACKWARD_WEIGHTS: [[i16; 8]; Colorimetry::Length as usize] = [ i32_to_i16(GP_709), i32_to_i16(BN_709), ], + [ + i32_to_i16(XXYM_601FR), + i32_to_i16(RCRM_601FR), + i32_to_i16(GCRM_601FR), + i32_to_i16(GCBM_601FR), + i32_to_i16(BCBM_601FR), + i32_to_i16(RN_601FR), + i32_to_i16(GP_601FR), + i32_to_i16(BN_601FR), + ], + [ + i32_to_i16(XXYM_709FR), + i32_to_i16(RCRM_709FR), + i32_to_i16(GCRM_709FR), + i32_to_i16(GCBM_709FR), + i32_to_i16(BCBM_709FR), + i32_to_i16(RN_709FR), + i32_to_i16(GP_709FR), + i32_to_i16(BN_709FR), + ], ]; /// Convert fixed point to int (4-wide) @@ -298,6 +342,7 @@ unsafe fn lrgb_to_yuv_4x( sampler: Sampler, y_weigths: &[__m128i; 3], uv_weights: &[__m128i; 3], + full_range: bool, ) { let (rg0, bg0) = unpack_ui8x3_i16x2_4x(rgb0, sampler); pack_i32_4x( @@ -313,10 +358,21 @@ unsafe fn lrgb_to_yuv_4x( let srg = sum_i16x2_neighborhood_2x(rg0, rg1); let sbg = sum_i16x2_neighborhood_2x(bg0, bg1); - pack_i32_4x( - uv, - fix_to_i32_4x!(affine_transform(srg, sbg, uv_weights), FIX18), - ); + let mut t = affine_transform(srg, sbg, uv_weights); + if full_range { + t = _mm_add_epi32( + t, + _mm_slli_epi32( + _mm_or_si128( + _mm_and_si128(sbg, _mm_set1_epi64x(0xFFFF_i64)), + _mm_and_si128(srg, _mm_set1_epi64x(0xFFFF_0000_0000_i64)), + ), + 14, + ), + ); + } + + pack_i32_4x(uv, fix_to_i32_4x!(t, FIX18)); } #[inline(always)] @@ -330,6 +386,7 @@ unsafe fn lrgb_to_i420_4x( sampler: Sampler, y_weigths: &[__m128i; 3], uv_weights: &[__m128i; 3], + full_range: bool, ) { let (rg0, bg0) = unpack_ui8x3_i16x2_4x(rgb0, sampler); pack_i32_4x( @@ -345,12 +402,22 @@ unsafe fn lrgb_to_i420_4x( let srg = sum_i16x2_neighborhood_2x(rg0, rg1); let sbg = sum_i16x2_neighborhood_2x(bg0, bg1); + let mut t = affine_transform(srg, sbg, uv_weights); + if full_range { + t = _mm_add_epi32( + t, + _mm_slli_epi32( + _mm_or_si128( + _mm_and_si128(sbg, _mm_set1_epi64x(0xFFFF_i64)), + _mm_and_si128(srg, _mm_set1_epi64x(0xFFFF_0000_0000_i64)), + ), + 14, + ), + ); + } // shuff: ******v1 ******v0 ******u1 ******u0 - let shuff = _mm_shuffle_epi32( - fix_to_i32_4x!(affine_transform(srg, sbg, uv_weights), FIX18), - mm_shuffle(3, 1, 2, 0), - ); + let shuff = _mm_shuffle_epi32(fix_to_i32_4x!(t, FIX18), mm_shuffle(3, 1, 2, 0)); // uv_res: v1v0u1u0 let packed_to_32 = _mm_packs_epi32(shuff, shuff); @@ -378,6 +445,7 @@ unsafe fn lrgb_to_i444_4x( y_weights: &[__m128i; 3], u_weights: &[__m128i; 3], v_weights: &[__m128i; 3], + full_range: bool, ) { let (rg, bg) = unpack_ui8x3_i16x2_4x(rgb, sampler); pack_i32_4x( @@ -385,15 +453,15 @@ unsafe fn lrgb_to_i444_4x( fix_to_i32_4x!(affine_transform(rg, bg, y_weights), FIX16), ); - pack_i32_4x( - u, - fix_to_i32_4x!(affine_transform(rg, bg, u_weights), FIX16), - ); + let mut tu = affine_transform(rg, bg, u_weights); + let mut tv = affine_transform(rg, bg, v_weights); + if full_range { + tu = _mm_add_epi32(tu, _mm_srli_epi32(_mm_slli_epi32(bg, 16), 2)); + tv = _mm_add_epi32(tv, _mm_srli_epi32(_mm_slli_epi32(rg, 16), 2)); + } - pack_i32_4x( - v, - fix_to_i32_4x!(affine_transform(rg, bg, v_weights), FIX16), - ); + pack_i32_4x(u, fix_to_i32_4x!(tu, FIX16)); + pack_i32_4x(v, fix_to_i32_4x!(tv, FIX16)); } #[inline] @@ -406,7 +474,7 @@ unsafe fn lrgb_to_yuv_sse2( dst_strides: (usize, usize), dst_buffers: &mut (&mut [u8], &mut [u8]), depth: usize, - weights: &[i32; 6], + weights: &[i32; 8], sampler: Sampler, ) { const DST_DEPTH: usize = LRGB_TO_YUV_WAVES; @@ -416,13 +484,13 @@ unsafe fn lrgb_to_yuv_sse2( let y_weigths = [ _mm_set1_epi32(weights[0]), _mm_set1_epi32(weights[1]), - _mm_set1_epi32(Y_OFFSET), + _mm_set1_epi32(weights[6]), ]; let uv_weights = [ _mm_set_epi32(weights[2], weights[3], weights[2], weights[3]), _mm_set_epi32(weights[4], weights[5], weights[4], weights[5]), - _mm_set1_epi32(C_OFFSET), + _mm_set1_epi32(C_OFFSET - FIX18_HALF * weights[7]), ]; let src_group = src_buffer.as_ptr(); @@ -451,6 +519,7 @@ unsafe fn lrgb_to_yuv_sse2( sampler, &y_weigths, &uv_weights, + weights[7] == 1, ); } } @@ -468,6 +537,7 @@ unsafe fn lrgb_to_yuv_sse2( sampler, &y_weigths, &uv_weights, + weights[7] == 1, ); } @@ -481,6 +551,7 @@ unsafe fn lrgb_to_yuv_sse2( Sampler::BgrOverflow, &y_weigths, &uv_weights, + weights[7] == 1, ); } } @@ -495,7 +566,7 @@ unsafe fn lrgb_to_i420_sse2( dst_strides: (usize, usize, usize), dst_buffers: &mut (&mut [u8], &mut [u8], &mut [u8]), depth: usize, - weights: &[i32; 6], + weights: &[i32; 8], sampler: Sampler, ) { let (y_stride, u_stride, v_stride) = dst_strides; @@ -503,13 +574,13 @@ unsafe fn lrgb_to_i420_sse2( let y_weigths = [ _mm_set1_epi32(weights[0]), _mm_set1_epi32(weights[1]), - _mm_set1_epi32(Y_OFFSET), + _mm_set1_epi32(weights[6]), ]; let uv_weights = [ _mm_set_epi32(weights[2], weights[3], weights[2], weights[3]), _mm_set_epi32(weights[4], weights[5], weights[4], weights[5]), - _mm_set1_epi32(C_OFFSET), + _mm_set1_epi32(C_OFFSET - FIX18_HALF * weights[7]), ]; let src_group = src_buffer.as_ptr(); @@ -540,6 +611,7 @@ unsafe fn lrgb_to_i420_sse2( sampler, &y_weigths, &uv_weights, + weights[7] == 1, ); } } @@ -558,6 +630,7 @@ unsafe fn lrgb_to_i420_sse2( sampler, &y_weigths, &uv_weights, + weights[7] == 1, ); } @@ -572,6 +645,7 @@ unsafe fn lrgb_to_i420_sse2( Sampler::BgrOverflow, &y_weigths, &uv_weights, + weights[7] == 1, ); } } @@ -586,7 +660,7 @@ unsafe fn lrgb_to_i444_sse2( dst_strides: (usize, usize, usize), dst_buffers: &mut (&mut [u8], &mut [u8], &mut [u8]), depth: usize, - weights: &[i32; 6], + weights: &[i32; 8], sampler: Sampler, ) { let (y_stride, u_stride, v_stride) = dst_strides; @@ -594,19 +668,19 @@ unsafe fn lrgb_to_i444_sse2( let y_weights = [ _mm_set1_epi32(weights[0]), _mm_set1_epi32(weights[1]), - _mm_set1_epi32(Y_OFFSET), + _mm_set1_epi32(weights[6]), ]; let u_weights = [ _mm_set1_epi32(weights[3]), _mm_set1_epi32(weights[5]), - _mm_set1_epi32(C_OFFSET16), + _mm_set1_epi32(C_OFFSET16 - FIX16_HALF * weights[7]), ]; let v_weights = [ _mm_set1_epi32(weights[2]), _mm_set1_epi32(weights[4]), - _mm_set1_epi32(C_OFFSET16), + _mm_set1_epi32(C_OFFSET16 - FIX16_HALF * weights[7]), ]; let src_group = src_buffer.as_ptr(); @@ -636,6 +710,7 @@ unsafe fn lrgb_to_i444_sse2( &y_weights, &u_weights, &v_weights, + weights[7] == 1, ); } } @@ -653,6 +728,7 @@ unsafe fn lrgb_to_i444_sse2( &y_weights, &u_weights, &v_weights, + weights[7] == 1, ); } @@ -666,6 +742,7 @@ unsafe fn lrgb_to_i444_sse2( &y_weights, &u_weights, &v_weights, + weights[7] == 1, ); } } @@ -2259,3 +2336,551 @@ pub fn bgra_lrgb_rgb_lrgb( dst_buffers, ) } + +pub fn argb_lrgb_nv12_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_nv12_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_nv12_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt601FR as usize, + Sampler::Bgr, + ) +} + +pub fn nv12_bt601fr_bgra_lrgb( + width: u32, + height: u32, + last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + nv12_bgra_lrgb( + width, + height, + last_src_plane as usize, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt601FR as usize, + ) +} + +pub fn i420_bt601fr_bgra_lrgb( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + i420_bgra_lrgb( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt601FR as usize, + ) +} + +pub fn i444_bt601fr_bgra_lrgb( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + i444_bgra_lrgb( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt601FR as usize, + ) +} + +pub fn argb_lrgb_i420_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_i420_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_i420_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt601FR as usize, + Sampler::Bgr, + ) +} + +pub fn argb_lrgb_i444_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_i444_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_i444_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt601FR as usize, + Sampler::Bgr, + ) +} + +pub fn argb_lrgb_nv12_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_nv12_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_nv12_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt709FR as usize, + Sampler::Bgr, + ) +} + +pub fn nv12_bt709fr_bgra_lrgb( + width: u32, + height: u32, + last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + nv12_bgra_lrgb( + width, + height, + last_src_plane as usize, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt709FR as usize, + ) +} + +pub fn i420_bt709fr_bgra_lrgb( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + i420_bgra_lrgb( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt709FR as usize, + ) +} + +pub fn i444_bt709fr_bgra_lrgb( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + i444_bgra_lrgb( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt709FR as usize, + ) +} + +pub fn argb_lrgb_i420_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_i420_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_i420_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt709FR as usize, + Sampler::Bgr, + ) +} + +pub fn argb_lrgb_i444_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_i444_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_i444_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt709FR as usize, + Sampler::Bgr, + ) +} diff --git a/src/convert_image/x86.rs b/src/convert_image/x86.rs index 1b4d76e..0969c44 100644 --- a/src/convert_image/x86.rs +++ b/src/convert_image/x86.rs @@ -51,12 +51,20 @@ unsafe fn _bswap64(x: i64) -> i64 { (((_bswap(x as i32) as u64) << 32) | ((_bswap((x >> 32) as i32) as u64) & 0xFFFFFFFF)) as i64 } -pub const FORWARD_WEIGHTS: [[i32; 9]; Colorimetry::Length as usize] = [ +pub const FORWARD_WEIGHTS: [[i32; 11]; Colorimetry::Length as usize] = [ [ - XR_601, XG_601, XB_601, YR_601, YG_601, YB_601, ZR_601, ZG_601, ZB_601, + XR_601, XG_601, XB_601, YR_601, YG_601, YB_601, ZR_601, ZG_601, ZB_601, Y_OFFSET, 0, ], [ - XR_709, XG_709, XB_709, YR_709, YG_709, YB_709, ZR_709, ZG_709, ZB_709, + XR_709, XG_709, XB_709, YR_709, YG_709, YB_709, ZR_709, ZG_709, ZB_709, Y_OFFSET, 0, + ], + [ + XR_601FR, XG_601FR, XB_601FR, YR_601FR, YG_601FR, YB_601FR, ZR_601FR, ZG_601FR, ZB_601FR, + FIX16_HALF, 1, + ], + [ + XR_709FR, XG_709FR, XB_709FR, YR_709FR, YG_709FR, YB_709FR, ZR_709FR, ZG_709FR, ZB_709FR, + FIX16_HALF, 1, ], ]; @@ -67,6 +75,12 @@ pub const BACKWARD_WEIGHTS: [[i32; 8]; Colorimetry::Length as usize] = [ [ XXYM_709, RCRM_709, GCRM_709, GCBM_709, BCBM_709, RN_709, GP_709, BN_709, ], + [ + XXYM_601FR, RCRM_601FR, GCRM_601FR, GCBM_601FR, BCBM_601FR, RN_601FR, GP_601FR, BN_601FR, + ], + [ + XXYM_709FR, RCRM_709FR, GCRM_709FR, GCBM_709FR, BCBM_709FR, RN_709FR, GP_709FR, BN_709FR, + ], ]; const SAMPLER_OFFSETS: [[usize; 3]; Sampler::Length as usize] = @@ -172,7 +186,7 @@ pub fn lrgb_to_nv12( dst_strides: (usize, usize), dst_buffers: &mut (&mut [u8], &mut [u8]), depth: usize, - weights: &[i32; 9], + weights: &[i32; 11], sampler: Sampler, ) { let (y_stride, uv_stride) = dst_strides; @@ -188,6 +202,8 @@ pub fn lrgb_to_nv12( let zr = weights[6]; let zg = weights[7]; let zb = weights[8]; + let yo = weights[9]; + let co = C_OFFSET - FIX18_HALF * weights[10]; let wg_width = width / 2; let wg_height = height / 2; @@ -211,8 +227,8 @@ pub fn lrgb_to_nv12( pack_i32x2( y_group.add(wg_index(2 * x, 2 * y, 1, y_stride)), - fix_to_i32(affine_transform(r00, g00, b00, xr, xg, xb, Y_OFFSET), FIX16), - fix_to_i32(affine_transform(r10, g10, b10, xr, xg, xb, Y_OFFSET), FIX16), + fix_to_i32(affine_transform(r00, g00, b00, xr, xg, xb, yo), FIX16), + fix_to_i32(affine_transform(r10, g10, b10, xr, xg, xb, yo), FIX16), ); let (r01, g01, b01) = unpack_ui8x3_i32( @@ -227,8 +243,8 @@ pub fn lrgb_to_nv12( pack_i32x2( y_group.add(wg_index(2 * x, 2 * y + 1, 1, y_stride)), - fix_to_i32(affine_transform(r01, g01, b01, xr, xg, xb, Y_OFFSET), FIX16), - fix_to_i32(affine_transform(r11, g11, b11, xr, xg, xb, Y_OFFSET), FIX16), + fix_to_i32(affine_transform(r01, g01, b01, xr, xg, xb, yo), FIX16), + fix_to_i32(affine_transform(r11, g11, b11, xr, xg, xb, yo), FIX16), ); let sr = (r00 + r10) + (r01 + r11); @@ -236,8 +252,8 @@ pub fn lrgb_to_nv12( let sb = (b00 + b10) + (b01 + b11); pack_i32x2( uv_group.add(wg_index(x, y, 2, uv_stride)), - fix_to_i32(affine_transform(sr, sg, sb, yr, yg, yb, C_OFFSET), FIX18), - fix_to_i32(affine_transform(sr, sg, sb, zr, zg, zb, C_OFFSET), FIX18), + fix_to_i32(affine_transform(sr, sg, sb, yr, yg, yb, co), FIX18), + fix_to_i32(affine_transform(sr, sg, sb, zr, zg, zb, co), FIX18), ); } } @@ -253,7 +269,7 @@ pub fn lrgb_to_i420( dst_strides: (usize, usize, usize), dst_buffers: &mut (&mut [u8], &mut [u8], &mut [u8]), depth: usize, - weights: &[i32; 9], + weights: &[i32; 11], sampler: Sampler, ) { let (y_stride, u_stride, v_stride) = dst_strides; @@ -267,6 +283,8 @@ pub fn lrgb_to_i420( let zr = weights[6]; let zg = weights[7]; let zb = weights[8]; + let yo = weights[9]; + let co = C_OFFSET - FIX18_HALF * weights[10]; let wg_width = width / 2; let wg_height = height / 2; @@ -291,8 +309,8 @@ pub fn lrgb_to_i420( pack_i32x2( y_group.add(wg_index(2 * x, 2 * y, 1, y_stride)), - fix_to_i32(affine_transform(r00, g00, b00, xr, xg, xb, Y_OFFSET), FIX16), - fix_to_i32(affine_transform(r10, g10, b10, xr, xg, xb, Y_OFFSET), FIX16), + fix_to_i32(affine_transform(r00, g00, b00, xr, xg, xb, yo), FIX16), + fix_to_i32(affine_transform(r10, g10, b10, xr, xg, xb, yo), FIX16), ); let (r01, g01, b01) = unpack_ui8x3_i32( @@ -307,8 +325,8 @@ pub fn lrgb_to_i420( pack_i32x2( y_group.add(wg_index(2 * x, 2 * y + 1, 1, y_stride)), - fix_to_i32(affine_transform(r01, g01, b01, xr, xg, xb, Y_OFFSET), FIX16), - fix_to_i32(affine_transform(r11, g11, b11, xr, xg, xb, Y_OFFSET), FIX16), + fix_to_i32(affine_transform(r01, g01, b01, xr, xg, xb, yo), FIX16), + fix_to_i32(affine_transform(r11, g11, b11, xr, xg, xb, yo), FIX16), ); let sr = (r00 + r10) + (r01 + r11); @@ -321,10 +339,8 @@ pub fn lrgb_to_i420( // Checked: this is proved to not go outside the 8-bit boundary #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] { - *u = - fix_to_i32(affine_transform(sr, sg, sb, yr, yg, yb, C_OFFSET), FIX18) as u8; - *v = - fix_to_i32(affine_transform(sr, sg, sb, zr, zg, zb, C_OFFSET), FIX18) as u8; + *u = fix_to_i32(affine_transform(sr, sg, sb, yr, yg, yb, co), FIX18) as u8; + *v = fix_to_i32(affine_transform(sr, sg, sb, zr, zg, zb, co), FIX18) as u8; } } } @@ -340,7 +356,7 @@ pub fn lrgb_to_i444( dst_strides: (usize, usize, usize), dst_buffers: &mut (&mut [u8], &mut [u8], &mut [u8]), depth: usize, - weights: &[i32; 9], + weights: &[i32; 11], sampler: Sampler, ) { let (y_stride, u_stride, v_stride) = dst_strides; @@ -354,6 +370,8 @@ pub fn lrgb_to_i444( let zr = weights[6]; let zg = weights[7]; let zb = weights[8]; + let yo = weights[9]; + let co = C_OFFSET16 - FIX16_HALF * weights[10]; unsafe { let src_group = src_buffer.as_ptr(); @@ -373,12 +391,9 @@ pub fn lrgb_to_i444( // Checked: this is proved to not go outside the 8-bit boundary #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] { - *y_data = - fix_to_i32(affine_transform(r, g, b, xr, xg, xb, Y_OFFSET), FIX16) as u8; - *u_data = - fix_to_i32(affine_transform(r, g, b, yr, yg, yb, C_OFFSET16), FIX16) as u8; - *v_data = - fix_to_i32(affine_transform(r, g, b, zr, zg, zb, C_OFFSET16), FIX16) as u8; + *y_data = fix_to_i32(affine_transform(r, g, b, xr, xg, xb, yo), FIX16) as u8; + *u_data = fix_to_i32(affine_transform(r, g, b, yr, yg, yb, co), FIX16) as u8; + *v_data = fix_to_i32(affine_transform(r, g, b, zr, zg, zb, co), FIX16) as u8; } } } @@ -1884,3 +1899,551 @@ pub fn bgra_lrgb_rgb_lrgb( true } + +pub fn argb_lrgb_nv12_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_nv12_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_nv12_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt601FR as usize, + Sampler::Bgr, + ) +} + +pub fn nv12_bt601fr_bgra_lrgb( + width: u32, + height: u32, + last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + nv12_bgra_lrgb( + width, + height, + last_src_plane as usize, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt601FR as usize, + ) +} + +pub fn i420_bt601fr_bgra_lrgb( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + i420_bgra_lrgb( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt601FR as usize, + ) +} + +pub fn i444_bt601fr_bgra_lrgb( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + i444_bgra_lrgb( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt601FR as usize, + ) +} + +pub fn argb_lrgb_i420_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_i420_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_i420_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt601FR as usize, + Sampler::Bgr, + ) +} + +pub fn argb_lrgb_i444_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_i444_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt601FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_i444_bt601fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt601FR as usize, + Sampler::Bgr, + ) +} + +pub fn argb_lrgb_nv12_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_nv12_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_nv12_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_nv12( + width, + height, + src_strides, + src_buffers, + last_dst_plane as usize, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt709FR as usize, + Sampler::Bgr, + ) +} + +pub fn nv12_bt709fr_bgra_lrgb( + width: u32, + height: u32, + last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + nv12_bgra_lrgb( + width, + height, + last_src_plane as usize, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt709FR as usize, + ) +} + +pub fn i420_bt709fr_bgra_lrgb( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + i420_bgra_lrgb( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt709FR as usize, + ) +} + +pub fn i444_bt709fr_bgra_lrgb( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + i444_bgra_lrgb( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + Colorimetry::Bt709FR as usize, + ) +} + +pub fn argb_lrgb_i420_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_i420_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_i420_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i420( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt709FR as usize, + Sampler::Bgr, + ) +} + +pub fn argb_lrgb_i444_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Argb, + ) +} + +pub fn bgra_lrgb_i444_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Four, + Colorimetry::Bt709FR as usize, + Sampler::Bgra, + ) +} + +pub fn bgr_lrgb_i444_bt709fr( + width: u32, + height: u32, + _last_src_plane: u32, + src_strides: &[usize], + src_buffers: &[&[u8]], + _last_dst_plane: u32, + dst_strides: &[usize], + dst_buffers: &mut [&mut [u8]], +) -> bool { + lrgb_i444( + width, + height, + src_strides, + src_buffers, + dst_strides, + dst_buffers, + PixelFormatChannels::Three, + Colorimetry::Bt709FR as usize, + Sampler::Bgr, + ) +} diff --git a/src/dispatcher.rs b/src/dispatcher.rs index 61131ec..efd1432 100644 --- a/src/dispatcher.rs +++ b/src/dispatcher.rs @@ -36,7 +36,7 @@ static_assert!(HI_RGB_PIXEL_FORMAT == LO_YUV_PIXEL_FORMAT - 1); const LO_RGB_COLOR_SPACE: u32 = ColorSpace::Lrgb as u32; const HI_RGB_COLOR_SPACE: u32 = ColorSpace::Lrgb as u32; const LO_YUV_COLOR_SPACE: u32 = ColorSpace::Bt601 as u32; -const HI_YUV_COLOR_SPACE: u32 = ColorSpace::Bt709 as u32; +const HI_YUV_COLOR_SPACE: u32 = ColorSpace::Bt709FR as u32; static_assert!(HI_RGB_COLOR_SPACE == LO_YUV_COLOR_SPACE - 1); const RGB_PIXEL_FORMAT_COUNT: u32 = enum_count(LO_RGB_PIXEL_FORMAT, HI_RGB_PIXEL_FORMAT); diff --git a/src/lib.rs b/src/lib.rs index 0746cad..fa0bd55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,6 +54,8 @@ //! * ycbcr, ITU-R Recommendation BT.601 (standard video system) //! * ycbcr, ITU-R Recommendation BT.709 (CSC systems) //! +//! Both standard range (0-235) and full range (0-255) are supported. +//! //! # Examples //! //! Initialize the library: @@ -295,6 +297,7 @@ mod pixel_format; mod static_assert; use cpu_info::{CpuManufacturer, InstructionSet}; +use paste::paste; use std::error; use std::fmt; @@ -358,10 +361,10 @@ impl error::Error for ErrorKind { /// `PixelFormat::Bgr` | `ColorSpace::Lrgb` /// `PixelFormat::Rgba` | `ColorSpace::Lrgb` /// `PixelFormat::Rgb` | `ColorSpace::Lrgb` -/// `PixelFormat::I444` | `ColorSpace::Bt601`, `ColorSpace::Bt709` -/// `PixelFormat::I422` | `ColorSpace::Bt601`, `ColorSpace::Bt709` -/// `PixelFormat::I420` | `ColorSpace::Bt601`, `ColorSpace::Bt709` -/// `PixelFormat::Nv12` | `ColorSpace::Bt601`, `ColorSpace::Bt709` +/// `PixelFormat::I444` | `ColorSpace::Bt601(FR)`, `ColorSpace::Bt709(FR)` +/// `PixelFormat::I422` | `ColorSpace::Bt601(FR)`, `ColorSpace::Bt709(FR)` +/// `PixelFormat::I420` | `ColorSpace::Bt601(FR)`, `ColorSpace::Bt709(FR)` +/// `PixelFormat::Nv12` | `ColorSpace::Bt601(FR)`, `ColorSpace::Bt709(FR)` /// /// Some pixel formats might impose additional restrictions on the accepted number of /// planes and the image size: @@ -394,50 +397,77 @@ type ConvertDispatcher = fn(u32, u32, u32, &[usize], &[&[u8]], u32, &[usize], &mut [&mut [u8]]) -> bool; macro_rules! set_dispatcher { - ($conv:expr, $set:ident, $src_pf:ident, $src_cs:ident, $dst_pf:ident, $dst_cs:ident, $name:ident) => { - $conv[dispatcher::get_index( - dispatcher::get_image_index( - PixelFormat::$src_pf as u32, - ColorSpace::$src_cs as u32, - dispatcher::get_pixel_format_mode(PixelFormat::$src_pf as u32), - ), - dispatcher::get_image_index( - PixelFormat::$dst_pf as u32, - ColorSpace::$dst_cs as u32, - dispatcher::get_pixel_format_mode(PixelFormat::$dst_pf as u32), - ), - )] = Some(convert_image::$set::$name) + ($conv:expr, $set:ident, $src_pf:ident, $src_cs:ident, $dst_pf:ident, $dst_cs:ident) => { + paste! { + $conv[dispatcher::get_index( + dispatcher::get_image_index( + PixelFormat::$src_pf as u32, + ColorSpace::$src_cs as u32, + dispatcher::get_pixel_format_mode(PixelFormat::$src_pf as u32), + ), + dispatcher::get_image_index( + PixelFormat::$dst_pf as u32, + ColorSpace::$dst_cs as u32, + dispatcher::get_pixel_format_mode(PixelFormat::$dst_pf as u32), + ), + )] = Some(convert_image::$set::[<$src_pf:lower _ $src_cs:lower _ $dst_pf:lower _ $dst_cs:lower>]) + } }; } macro_rules! set_dispatch_table { ($conv:expr, $set:ident) => { - set_dispatcher!($conv, $set, Argb, Lrgb, Nv12, Bt601, argb_lrgb_nv12_bt601); - set_dispatcher!($conv, $set, Argb, Lrgb, Nv12, Bt709, argb_lrgb_nv12_bt709); - set_dispatcher!($conv, $set, Bgra, Lrgb, Nv12, Bt601, bgra_lrgb_nv12_bt601); - set_dispatcher!($conv, $set, Bgra, Lrgb, Nv12, Bt709, bgra_lrgb_nv12_bt709); - set_dispatcher!($conv, $set, Bgr, Lrgb, Nv12, Bt601, bgr_lrgb_nv12_bt601); - set_dispatcher!($conv, $set, Bgr, Lrgb, Nv12, Bt709, bgr_lrgb_nv12_bt709); - set_dispatcher!($conv, $set, Argb, Lrgb, I420, Bt601, argb_lrgb_i420_bt601); - set_dispatcher!($conv, $set, Argb, Lrgb, I420, Bt709, argb_lrgb_i420_bt709); - set_dispatcher!($conv, $set, Bgra, Lrgb, I420, Bt601, bgra_lrgb_i420_bt601); - set_dispatcher!($conv, $set, Bgra, Lrgb, I420, Bt709, bgra_lrgb_i420_bt709); - set_dispatcher!($conv, $set, Bgr, Lrgb, I420, Bt601, bgr_lrgb_i420_bt601); - set_dispatcher!($conv, $set, Bgr, Lrgb, I420, Bt709, bgr_lrgb_i420_bt709); - set_dispatcher!($conv, $set, Argb, Lrgb, I444, Bt601, argb_lrgb_i444_bt601); - set_dispatcher!($conv, $set, Argb, Lrgb, I444, Bt709, argb_lrgb_i444_bt709); - set_dispatcher!($conv, $set, Bgra, Lrgb, I444, Bt601, bgra_lrgb_i444_bt601); - set_dispatcher!($conv, $set, Bgra, Lrgb, I444, Bt709, bgra_lrgb_i444_bt709); - set_dispatcher!($conv, $set, Bgr, Lrgb, I444, Bt601, bgr_lrgb_i444_bt601); - set_dispatcher!($conv, $set, Bgr, Lrgb, I444, Bt709, bgr_lrgb_i444_bt709); - set_dispatcher!($conv, $set, Nv12, Bt601, Bgra, Lrgb, nv12_bt601_bgra_lrgb); - set_dispatcher!($conv, $set, Nv12, Bt709, Bgra, Lrgb, nv12_bt709_bgra_lrgb); - set_dispatcher!($conv, $set, Rgb, Lrgb, Bgra, Lrgb, rgb_lrgb_bgra_lrgb); - set_dispatcher!($conv, $set, I420, Bt601, Bgra, Lrgb, i420_bt601_bgra_lrgb); - set_dispatcher!($conv, $set, I420, Bt709, Bgra, Lrgb, i420_bt709_bgra_lrgb); - set_dispatcher!($conv, $set, I444, Bt601, Bgra, Lrgb, i444_bt601_bgra_lrgb); - set_dispatcher!($conv, $set, I444, Bt709, Bgra, Lrgb, i444_bt709_bgra_lrgb); - set_dispatcher!($conv, $set, Bgra, Lrgb, Rgb, Lrgb, bgra_lrgb_rgb_lrgb); + set_dispatcher!($conv, $set, Argb, Lrgb, I420, Bt601); + set_dispatcher!($conv, $set, Argb, Lrgb, I420, Bt709); + set_dispatcher!($conv, $set, Argb, Lrgb, I444, Bt601); + set_dispatcher!($conv, $set, Argb, Lrgb, I444, Bt709); + set_dispatcher!($conv, $set, Argb, Lrgb, Nv12, Bt601); + set_dispatcher!($conv, $set, Argb, Lrgb, Nv12, Bt709); + set_dispatcher!($conv, $set, Bgr, Lrgb, I420, Bt601); + set_dispatcher!($conv, $set, Bgr, Lrgb, I420, Bt709); + set_dispatcher!($conv, $set, Bgr, Lrgb, I444, Bt601); + set_dispatcher!($conv, $set, Bgr, Lrgb, I444, Bt709); + set_dispatcher!($conv, $set, Bgr, Lrgb, Nv12, Bt601); + set_dispatcher!($conv, $set, Bgr, Lrgb, Nv12, Bt709); + set_dispatcher!($conv, $set, Bgra, Lrgb, I420, Bt601); + set_dispatcher!($conv, $set, Bgra, Lrgb, I420, Bt709); + set_dispatcher!($conv, $set, Bgra, Lrgb, I444, Bt601); + set_dispatcher!($conv, $set, Bgra, Lrgb, I444, Bt709); + set_dispatcher!($conv, $set, Bgra, Lrgb, Nv12, Bt601); + set_dispatcher!($conv, $set, Bgra, Lrgb, Nv12, Bt709); + set_dispatcher!($conv, $set, Bgra, Lrgb, Rgb, Lrgb); + set_dispatcher!($conv, $set, I420, Bt601, Bgra, Lrgb); + set_dispatcher!($conv, $set, I420, Bt709, Bgra, Lrgb); + set_dispatcher!($conv, $set, I444, Bt601, Bgra, Lrgb); + set_dispatcher!($conv, $set, I444, Bt709, Bgra, Lrgb); + set_dispatcher!($conv, $set, Nv12, Bt601, Bgra, Lrgb); + set_dispatcher!($conv, $set, Nv12, Bt709, Bgra, Lrgb); + set_dispatcher!($conv, $set, Rgb, Lrgb, Bgra, Lrgb); + + set_dispatcher!($conv, $set, Argb, Lrgb, I420, Bt601FR); + set_dispatcher!($conv, $set, Argb, Lrgb, I420, Bt709FR); + set_dispatcher!($conv, $set, Argb, Lrgb, I444, Bt601FR); + set_dispatcher!($conv, $set, Argb, Lrgb, I444, Bt709FR); + set_dispatcher!($conv, $set, Argb, Lrgb, Nv12, Bt601FR); + set_dispatcher!($conv, $set, Argb, Lrgb, Nv12, Bt709FR); + set_dispatcher!($conv, $set, Bgr, Lrgb, I420, Bt601FR); + set_dispatcher!($conv, $set, Bgr, Lrgb, I420, Bt709FR); + set_dispatcher!($conv, $set, Bgr, Lrgb, I444, Bt601FR); + set_dispatcher!($conv, $set, Bgr, Lrgb, I444, Bt709FR); + set_dispatcher!($conv, $set, Bgr, Lrgb, Nv12, Bt601FR); + set_dispatcher!($conv, $set, Bgr, Lrgb, Nv12, Bt709FR); + set_dispatcher!($conv, $set, Bgra, Lrgb, I420, Bt601FR); + set_dispatcher!($conv, $set, Bgra, Lrgb, I420, Bt709FR); + set_dispatcher!($conv, $set, Bgra, Lrgb, I444, Bt601FR); + set_dispatcher!($conv, $set, Bgra, Lrgb, I444, Bt709FR); + set_dispatcher!($conv, $set, Bgra, Lrgb, Nv12, Bt601FR); + set_dispatcher!($conv, $set, Bgra, Lrgb, Nv12, Bt709FR); + set_dispatcher!($conv, $set, I420, Bt601FR, Bgra, Lrgb); + set_dispatcher!($conv, $set, I420, Bt709FR, Bgra, Lrgb); + set_dispatcher!($conv, $set, I444, Bt601FR, Bgra, Lrgb); + set_dispatcher!($conv, $set, I444, Bt709FR, Bgra, Lrgb); + set_dispatcher!($conv, $set, Nv12, Bt601FR, Bgra, Lrgb); + set_dispatcher!($conv, $set, Nv12, Bt709FR, Bgra, Lrgb); }; } @@ -791,6 +821,18 @@ pub fn get_buffers_size( /// y = 0.213 * r + 0.715 * g + 0.072 * b + 16 /// cb = -0.117 * r - 0.394 * g + 0.511 * b + 128 /// cr = 0.511 * r - 0.464 * g - 0.047 * b + 128 +/// +/// If the destination image color space is Bt601FR, the following formula is applied: +/// ```text +/// y = 0.299 * r + 0.587 * g + 0.114 * b +/// cb = -0.169 * r - 0.331 * g + 0.500 * b + 128 +/// cr = 0.500 * r - 0.419 * g - 0.081 * b + 128 +/// +/// If the destination image color space is Bt709FR, the following formula is applied: +/// ```text +/// y = 0.213 * r + 0.715 * g + 0.072 * b +/// cb = -0.115 * r - 0.385 * g + 0.500 * b + 128 +/// cr = 0.500 * r - 0.454 * g - 0.046 * b + 128 /// ``` /// /// # Algorithm 2 @@ -810,6 +852,18 @@ pub fn get_buffers_size( /// r = 1.164 * (y - 16) + 1.793 * (cr - 128) /// g = 1.164 * (y - 16) - 0.534 * (cr - 128) - 0.213 * (cb - 128) /// b = 1.164 * (y - 16) + 2.115 * (cb - 128) +/// +/// If the source image color space is Bt601FR, the following formula is applied: +/// ```text +/// r = y + 1.402 * (cr - 128) +/// g = y - 0.714 * (cr - 128) - 0.344 * (cb - 128) +/// b = y + 1.772 * (cb - 128) +/// +/// If the source image color space is Bt709FR, the following formula is applied: +/// ```text +/// r = y + 1.575 * (cr - 128) +/// g = y - 0.468 * (cr - 128) - 0.187 * (cb - 128) +/// b = y + 1.856 * (cb - 128) /// ``` /// /// # Algorithm 3 diff --git a/tests/tests.rs b/tests/tests.rs index 4d372f5..fdb8080 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -56,7 +56,13 @@ const PIXEL_FORMATS: &[PixelFormat; 9] = &[ PixelFormat::Nv12, ]; -const COLOR_SPACES: &[ColorSpace; 3] = &[ColorSpace::Lrgb, ColorSpace::Bt601, ColorSpace::Bt709]; +const COLOR_SPACES: &[ColorSpace; 5] = &[ + ColorSpace::Lrgb, + ColorSpace::Bt601, + ColorSpace::Bt709, + ColorSpace::Bt601FR, + ColorSpace::Bt709FR, +]; const PIXEL_FORMAT_I444: u32 = PixelFormat::I444 as u32; const PIXEL_FORMAT_I422: u32 = PixelFormat::I422 as u32; @@ -242,6 +248,100 @@ const CR2_BT709_REF: &[&[u8]] = &[ &[146, 131, 135, 127], ]; +const Y_BT601FR_REF: &[&[u8]] = &[ + &[67, 72, 99, 136, 119, 138, 83, 222], + &[90, 181, 116, 91, 100, 98, 180, 123], + &[146, 68, 214, 231, 95, 135, 57, 153], + &[145, 45, 198, 185, 161, 196, 75, 90], + &[119, 89, 127, 157, 28, 173, 192, 187], + &[144, 89, 110, 112, 135, 114, 165, 55], + &[113, 201, 99, 84, 104, 90, 130, 159], + &[134, 57, 37, 69, 132, 170, 118, 145], +]; + +const CB_BT601FR_REF: &[&[u8]] = &[ + &[114, 116, 214, 86, 136, 162, 92, 95], + &[95, 40, 185, 130, 165, 95, 115, 67], + &[53, 119, 144, 83, 149, 159, 191, 54], + &[117, 180, 134, 157, 133, 100, 192, 184], + &[201, 97, 199, 98, 200, 130, 74, 31], + &[50, 80, 208, 74, 181, 80, 52, 105], + &[89, 49, 121, 215, 77, 204, 123, 152], + &[192, 213, 192, 93, 141, 35, 205, 86], +]; + +const CR_BT601FR_REF: &[&[u8]] = &[ + &[194, 101, 183, 207, 219, 99, 117, 136], + &[242, 166, 149, 80, 72, 234, 116, 108], + &[195, 79, 113, 117, 142, 167, 171, 174], + &[168, 126, 83, 177, 77, 77, 74, 86], + &[190, 98, 68, 75, 119, 185, 92, 170], + &[76, 221, 223, 161, 190, 51, 82, 116], + &[135, 158, 87, 189, 62, 132, 182, 104], + &[161, 133, 118, 121, 194, 156, 56, 159], +]; + +const CB2_BT601FR_REF: &[&[u8]] = &[ + &[91, 154, 140, 93], + &[117, 130, 135, 155], + &[107, 145, 148, 65], + &[136, 155, 114, 142], +]; + +const CR2_BT601FR_REF: &[&[u8]] = &[ + &[176, 155, 156, 119], + &[142, 122, 116, 126], + &[146, 132, 136, 115], + &[147, 129, 136, 125], +]; + +const Y_BT709FR_REF: &[&[u8]] = &[ + &[55, 79, 77, 124, 99, 140, 90, 224], + &[69, 183, 105, 101, 108, 80, 184, 135], + &[140, 79, 215, 239, 89, 123, 40, 152], + &[138, 39, 206, 171, 171, 210, 79, 92], + &[97, 98, 131, 172, 21, 160, 206, 189], + &[164, 75, 80, 111, 115, 136, 183, 60], + &[116, 204, 108, 60, 124, 80, 119, 162], + &[119, 46, 31, 75, 116, 175, 124, 143], +]; + +const CB_BT709FR_REF: &[&[u8]] = &[ + &[122, 112, 222, 94, 147, 160, 90, 96], + &[107, 43, 189, 125, 160, 106, 114, 64], + &[60, 113, 143, 81, 151, 164, 197, 58], + &[121, 180, 129, 163, 127, 94, 187, 180], + &[209, 93, 193, 91, 201, 137, 69, 34], + &[42, 90, 220, 77, 189, 71, 45, 104], + &[90, 51, 116, 223, 68, 206, 129, 150], + &[197, 215, 192, 92, 149, 37, 198, 89], +]; + +const CR_BT709FR_REF: &[&[u8]] = &[ + &[195, 100, 191, 206, 222, 101, 114, 133], + &[243, 161, 154, 79, 73, 234, 115, 103], + &[191, 77, 114, 113, 144, 170, 177, 170], + &[168, 130, 83, 180, 76, 73, 78, 89], + &[197, 95, 72, 71, 124, 186, 87, 164], + &[69, 220, 232, 158, 195, 45, 75, 115], + &[132, 153, 86, 197, 57, 138, 183, 105], + &[167, 140, 123, 118, 197, 150, 60, 156], +]; + +const CB2_BT709FR_REF: &[&[u8]] = &[ + &[96, 157, 143, 91], + &[119, 129, 134, 156], + &[109, 145, 149, 63], + &[138, 156, 115, 142], +]; + +const CR2_BT709FR_REF: &[&[u8]] = &[ + &[175, 157, 157, 116], + &[142, 122, 116, 128], + &[145, 133, 138, 110], + &[148, 131, 135, 126], +]; + // Largest group that uses neither avx2 nor sse2 is 62x64. // We can arrange the image as blocks: // y0 y0 | y1 y1 | ... @@ -271,19 +371,47 @@ const CR2_BT709_REF: &[&[u8]] = &[ // magenta (255, 0, 255): 78, 214, 230 // cyan ( 0, 255, 255): 188, 154, 16 // white (255, 255, 255): 235, 128, 128 -const Y_SRC: [[u8; 8]; 2] = [ +// +// Color table (bt601 full range): +// r g b +// black ( 0, 0, 0): 0, 128, 128 +// red (255, 0, 0): 76, 84, 255 +// green ( 0, 255, 0): 149, 43, 21 +// yellow (255, 255, 0): 225, 0, 148 +// blue ( 0, 0, 255): 29, 255, 107 +// magenta (255, 0, 255): 105, 212, 234 +// cyan ( 0, 255, 255): 178, 171, 0 +// white (255, 255, 255): 255, 128, 128 +// +// Color table (bt709 full range): +// r g b +// black ( 0, 0, 0): 0, 128, 128 +// red (255, 0, 0): 54, 98, 255 +// green ( 0, 255, 0): 182, 29, 12 +// yellow (255, 255, 0): 237, 0, 139 +// blue ( 0, 0, 255): 18, 255, 116 +// magenta (255, 0, 255): 73, 226, 243 +// cyan ( 0, 255, 255): 201, 157, 0 +// white (255, 255, 255): 255, 128, 128 +const Y_SRC: [[u8; 8]; 4] = [ [16, 82, 145, 210, 41, 107, 169, 235], [16, 63, 173, 219, 32, 78, 188, 235], + [0, 76, 149, 225, 29, 105, 178, 255], + [0, 54, 182, 237, 18, 73, 201, 255], ]; -const U_SRC: [[u8; 8]; 2] = [ +const U_SRC: [[u8; 8]; 4] = [ [128, 90, 54, 16, 240, 202, 166, 128], [128, 102, 42, 16, 240, 214, 154, 128], + [128, 84, 43, 0, 255, 212, 171, 128], + [128, 98, 29, 0, 255, 226, 157, 128], ]; -const V_SRC: [[u8; 8]; 2] = [ +const V_SRC: [[u8; 8]; 4] = [ [128, 240, 34, 146, 110, 222, 16, 128], [128, 240, 26, 138, 118, 230, 16, 128], + [128, 255, 21, 148, 107, 234, 0, 128], + [128, 255, 12, 139, 116, 243, 0, 128], ]; const NUM_LOG2_DEN: [[usize; 2]; 9] = [ @@ -727,7 +855,12 @@ fn rgb_to_yuv_size( } fn rgb_to_yuv_ok(pixel_format: PixelFormat, num_planes: u32) { - const SUPPORTED_COLOR_SPACES: &[ColorSpace] = &[ColorSpace::Bt601, ColorSpace::Bt709]; + const SUPPORTED_COLOR_SPACES: &[ColorSpace] = &[ + ColorSpace::Bt601, + ColorSpace::Bt709, + ColorSpace::Bt601FR, + ColorSpace::Bt709FR, + ]; const MAX_WIDTH: u32 = 8; const MAX_HEIGHT: u32 = 8; @@ -746,12 +879,16 @@ fn rgb_to_yuv_ok(pixel_format: PixelFormat, num_planes: u32) { let plane_ref = if let PixelFormat::I444 = pixel_format { match color_space { ColorSpace::Bt601 => (Y_BT601_REF, CB_BT601_REF, CR_BT601_REF), - _ => (Y_BT709_REF, CB_BT709_REF, CR_BT709_REF), + ColorSpace::Bt709 => (Y_BT709_REF, CB_BT709_REF, CR_BT709_REF), + ColorSpace::Bt601FR => (Y_BT601FR_REF, CB_BT601FR_REF, CR_BT601FR_REF), + _ => (Y_BT709FR_REF, CB_BT709FR_REF, CR_BT709FR_REF), } } else { match color_space { ColorSpace::Bt601 => (Y_BT601_REF, CB2_BT601_REF, CR2_BT601_REF), - _ => (Y_BT709_REF, CB2_BT709_REF, CR2_BT709_REF), + ColorSpace::Bt709 => (Y_BT709_REF, CB2_BT709_REF, CR2_BT709_REF), + ColorSpace::Bt601FR => (Y_BT601FR_REF, CB2_BT601FR_REF, CR2_BT601FR_REF), + _ => (Y_BT709FR_REF, CB2_BT709FR_REF, CR2_BT709FR_REF), } }; @@ -801,7 +938,9 @@ fn yuv_to_rgb_size_format_mode_stride( let dst_size = dst_stride * h; let color_space_index = match src_format.color_space { ColorSpace::Bt601 => 0, - _ => 1, + ColorSpace::Bt709 => 1, + ColorSpace::Bt601FR => 2, + _ => 3, }; // Allocate and initialize input @@ -1005,7 +1144,12 @@ fn yuv_to_rgb_size_format_mode( } fn yuv_to_rgb_ok(pixel_format: PixelFormat, num_planes: u32) { - const SUPPORTED_COLOR_SPACES: &[ColorSpace] = &[ColorSpace::Bt601, ColorSpace::Bt709]; + const SUPPORTED_COLOR_SPACES: &[ColorSpace] = &[ + ColorSpace::Bt601, + ColorSpace::Bt709, + ColorSpace::Bt601FR, + ColorSpace::Bt709FR, + ]; const MAX_WIDTH: u32 = 34; const MAX_HEIGHT: u32 = 4;