|
47 | 47 | #include <stdint.h> // for uint32_t, uintptr_t, uint16_t, uint64_t |
48 | 48 | #include <string.h> // for memcpy |
49 | 49 |
|
| 50 | +#include "color_space.h" |
50 | 51 | #include "compat/c23.h" // for size_t, NULL, countof, nullptr, ptrdiff_t |
51 | 52 | #include "types.h" // for depth |
52 | 53 | #include "utils/macros.h" // for OPTIMIZED_FOR |
@@ -897,3 +898,225 @@ yuv444p16le_to_v210(struct from_planar_data d) |
897 | 898 | yuv444p1Xle_to_v210(d, DEPTH16); |
898 | 899 | } |
899 | 900 |
|
| 901 | +#if defined __GNUC__ |
| 902 | +// actually faster with -02 with GCC |
| 903 | +#if !defined __clang__ |
| 904 | +#pragma GCC push_options |
| 905 | +#pragma GCC optimize("O2") |
| 906 | +#endif |
| 907 | +#endif |
| 908 | +[[gnu::always_inline]] static inline void |
| 909 | +yuv444pXXle_to_r12l(struct from_planar_data d, int depth) |
| 910 | +{ |
| 911 | + const int width = d.width; |
| 912 | + const size_t height = d.height; |
| 913 | + |
| 914 | + assert((uintptr_t) d.in_linesize[0] % 2 == 0); |
| 915 | + assert((uintptr_t) d.in_linesize[1] % 2 == 0); |
| 916 | + assert((uintptr_t) d.in_linesize[2] % 2 == 0); |
| 917 | + |
| 918 | + int lmt_rng = d.in_ycbcr_full_rng ? 0 : depth; |
| 919 | + const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, lmt_rng); |
| 920 | + for (size_t y = 0; y < height; ++y) { |
| 921 | + const uint16_t *src_y = (const void *) (d.in_data[0] + (d.in_linesize[0] * y)); |
| 922 | + const uint16_t *src_cb = (const void *) (d.in_data[1] + (d.in_linesize[1] * y)); |
| 923 | + const uint16_t *src_cr = (const void *) (d.in_data[2] + (d.in_linesize[2] * y)); |
| 924 | + unsigned char *dst = d.out_data + (y * d.out_pitch); |
| 925 | + |
| 926 | + for (int x = 0; x < width; x += 8) { |
| 927 | + comp_type_t r[8]; |
| 928 | + comp_type_t g[8]; |
| 929 | + comp_type_t b[8]; |
| 930 | + OPTIMIZED_FOR (int j = 0; j < 8; ++j) { |
| 931 | + comp_type_t y = (cfs.y_scale * (*src_y++ - (1<<(depth-4)))); |
| 932 | + comp_type_t cr = *src_cr++ - (1<<(depth-1)); |
| 933 | + comp_type_t cb = *src_cb++ - (1<<(depth-1)); |
| 934 | + comp_type_t rr = YCBCR_TO_R(cfs, y, cb, cr) >> (COMP_BASE-12+depth); |
| 935 | + comp_type_t gg = YCBCR_TO_G(cfs, y, cb, cr) >> (COMP_BASE-12+depth); |
| 936 | + comp_type_t bb = YCBCR_TO_B(cfs, y, cb, cr) >> (COMP_BASE-12+depth); |
| 937 | + r[j] = CLAMP_FULL(rr, 12); |
| 938 | + g[j] = CLAMP_FULL(gg, 12); |
| 939 | + b[j] = CLAMP_FULL(bb, 12); |
| 940 | + } |
| 941 | + |
| 942 | + dst[0] = r[0] & 0xff; |
| 943 | + dst[1] = (g[0] & 0xf) << 4 | r[0] >> 8; |
| 944 | + dst[2] = g[0] >> 4; |
| 945 | + dst[3] = b[0] & 0xff; |
| 946 | + dst[4 + 0] = (r[1] & 0xf) << 4 | b[0] >> 8; |
| 947 | + dst[4 + 1] = r[1] >> 4; |
| 948 | + dst[4 + 2] = g[1] & 0xff; |
| 949 | + dst[4 + 3] = (b[1] & 0xf) << 4 | g[1] >> 8; |
| 950 | + dst[8 + 0] = b[1] >> 4; |
| 951 | + dst[8 + 1] = r[2] & 0xff; |
| 952 | + dst[8 + 2] = (g[2] & 0xf) << 4 | r[2] >> 8; |
| 953 | + dst[8 + 3] = g[2] >> 4; |
| 954 | + dst[12 + 0] = b[2] & 0xff; |
| 955 | + dst[12 + 1] = (r[3] & 0xf) << 4 | b[2] >> 8; |
| 956 | + dst[12 + 2] = r[3] >> 4; |
| 957 | + dst[12 + 3] = g[3] & 0xff; |
| 958 | + dst[16 + 0] = (b[3] & 0xf) << 4 | g[3] >> 8; |
| 959 | + dst[16 + 1] = b[3] >> 4; |
| 960 | + dst[16 + 2] = r[4] & 0xff; |
| 961 | + dst[16 + 3] = (g[4] & 0xf) << 4 | r[4] >> 8; |
| 962 | + dst[20 + 0] = g[4] >> 4; |
| 963 | + dst[20 + 1] = b[4] & 0xff; |
| 964 | + dst[20 + 2] = (r[5] & 0xf) << 4 | b[4] >> 8; |
| 965 | + dst[20 + 3] = r[5] >> 4;; |
| 966 | + dst[24 + 0] = g[5] & 0xff; |
| 967 | + dst[24 + 1] = (b[5] & 0xf) << 4 | g[5] >> 8; |
| 968 | + dst[24 + 2] = b[5] >> 4; |
| 969 | + dst[24 + 3] = r[6] & 0xff; |
| 970 | + dst[28 + 0] = (g[6] & 0xf) << 4 | r[6] >> 8; |
| 971 | + dst[28 + 1] = g[6] >> 4; |
| 972 | + dst[28 + 2] = b[6] & 0xff; |
| 973 | + dst[28 + 3] = (r[7] & 0xf) << 4 | b[6] >> 8; |
| 974 | + dst[32 + 0] = r[7] >> 4; |
| 975 | + dst[32 + 1] = g[7] & 0xff; |
| 976 | + dst[32 + 2] = (b[7] & 0xf) << 4 | g[7] >> 8; |
| 977 | + dst[32 + 3] = b[7] >> 4; |
| 978 | + dst += 36; |
| 979 | + } |
| 980 | + } |
| 981 | +} |
| 982 | +void |
| 983 | +yuv444p10le_to_r12l(struct from_planar_data d) |
| 984 | +{ |
| 985 | + yuv444pXXle_to_r12l(d, DEPTH10); |
| 986 | +} |
| 987 | + |
| 988 | +void |
| 989 | +yuv444p12le_to_r12l(struct from_planar_data d) |
| 990 | +{ |
| 991 | + yuv444pXXle_to_r12l(d, DEPTH12); |
| 992 | +} |
| 993 | +void |
| 994 | +yuv444p16le_to_r12l(struct from_planar_data d) |
| 995 | +{ |
| 996 | + yuv444pXXle_to_r12l(d, DEPTH16); |
| 997 | +} |
| 998 | +#if defined __GNUC__ && !defined __clang__ |
| 999 | +#pragma GCC pop_options |
| 1000 | +#endif |
| 1001 | + |
| 1002 | +[[gnu::always_inline]] static inline void |
| 1003 | +yuv444pXXle_to_r10k(struct from_planar_data d, int depth) |
| 1004 | +{ |
| 1005 | + const int width = d.width; |
| 1006 | + const size_t height = d.height; |
| 1007 | + |
| 1008 | + assert((uintptr_t) d.in_linesize[0] % 2 == 0); |
| 1009 | + assert((uintptr_t) d.in_linesize[1] % 2 == 0); |
| 1010 | + assert((uintptr_t) d.in_linesize[2] % 2 == 0); |
| 1011 | + |
| 1012 | + int lmt_rng = d.in_ycbcr_full_rng ? 0 : depth; |
| 1013 | + const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, lmt_rng); |
| 1014 | + for (size_t y = 0; y < height; ++y) { |
| 1015 | + const uint16_t *src_y = (const void *) (d.in_data[0] + (d.in_linesize[0] * y)); |
| 1016 | + const uint16_t *src_cb = (const void *) (d.in_data[1] + (d.in_linesize[1] * y)); |
| 1017 | + const uint16_t *src_cr = (const void *) (d.in_data[2] + (d.in_linesize[2] * y)); |
| 1018 | + unsigned char *dst = d.out_data + (y * d.out_pitch); |
| 1019 | + |
| 1020 | + OPTIMIZED_FOR (int x = 0; x < width; ++x) { |
| 1021 | + comp_type_t y = (cfs.y_scale * (*src_y++ - (1<<(depth-4)))); |
| 1022 | + comp_type_t cr = *src_cr++ - (1<<(depth-1)); |
| 1023 | + comp_type_t cb = *src_cb++ - (1<<(depth-1)); |
| 1024 | + |
| 1025 | + comp_type_t r = YCBCR_TO_R(cfs, y, cb, cr) >> (COMP_BASE-10+depth); |
| 1026 | + comp_type_t g = YCBCR_TO_G(cfs, y, cb, cr) >> (COMP_BASE-10+depth); |
| 1027 | + comp_type_t b = YCBCR_TO_B(cfs, y, cb, cr) >> (COMP_BASE-10+depth); |
| 1028 | + // r g b is now on 10 bit scale |
| 1029 | + |
| 1030 | + r = CLAMP_FULL(r, 10); |
| 1031 | + g = CLAMP_FULL(g, 10); |
| 1032 | + b = CLAMP_FULL(b, 10); |
| 1033 | + |
| 1034 | + *dst++ = r >> 2; |
| 1035 | + *dst++ = (r & 0x3) << 6 | g >> 4; |
| 1036 | + *dst++ = (g & 0xF) << 4 | b >> 6; |
| 1037 | + *dst++ = (b & 0x3F) << 2 | 0x3U; |
| 1038 | + } |
| 1039 | + } |
| 1040 | +} |
| 1041 | +void |
| 1042 | +yuv444p10le_to_r10k(struct from_planar_data d) |
| 1043 | +{ |
| 1044 | + yuv444pXXle_to_r10k(d, DEPTH10); |
| 1045 | +} |
| 1046 | +void |
| 1047 | +yuv444p12le_to_r10k(struct from_planar_data d) |
| 1048 | +{ |
| 1049 | + yuv444pXXle_to_r10k(d, DEPTH12); |
| 1050 | +} |
| 1051 | +void |
| 1052 | +yuv444p16le_to_r10k(struct from_planar_data d) |
| 1053 | +{ |
| 1054 | + yuv444pXXle_to_r10k(d, DEPTH16); |
| 1055 | +} |
| 1056 | + |
| 1057 | +#define MK_RGBA(r, g, b, alpha_mask, depth) \ |
| 1058 | + FORMAT_RGBA((r), (g), (b), (d.rgb_shift)[0], (d.rgb_shift)[1], \ |
| 1059 | + (d.rgb_shift)[2], (alpha_mask), (depth)) |
| 1060 | + |
| 1061 | +/** |
| 1062 | + * @note |
| 1063 | + * Input alpha (if any) is always discarded. This is due to "special" RGB-shifts |
| 1064 | + * handling inside UG for output RGBA that makes handling true alpha difficult. |
| 1065 | + * In general, we can get rshift=0, gshift=10, bshift=20, meaning that there is |
| 1066 | + * no space for alpha. If needed, it could be implemented in an opportunistic |
| 1067 | + * way, however, eg. if RGB shifts are the default. |
| 1068 | + */ |
| 1069 | +[[gnu::always_inline]] static inline void |
| 1070 | +yuv444p10le_to_rgb(struct from_planar_data d, bool out_rgba) |
| 1071 | +{ |
| 1072 | + const int width = d.width; |
| 1073 | + const size_t height = d.height; |
| 1074 | + |
| 1075 | + enum { |
| 1076 | + S_DEPTH = DEPTH10, |
| 1077 | + }; |
| 1078 | + const uint32_t alpha_mask = 0xFFFFFFFFU ^ (0xFFU << d.rgb_shift[0]) ^ |
| 1079 | + (0xFFU << d.rgb_shift[1]) ^ |
| 1080 | + (0xFFU << d.rgb_shift[2]); |
| 1081 | + int lmt_rng = d.in_ycbcr_full_rng ? 0 : S_DEPTH; |
| 1082 | + const struct color_coeffs cfs = *get_color_coeffs(d.cs_coeffs, lmt_rng); |
| 1083 | + |
| 1084 | + for (size_t y = 0; y < height; y++) { |
| 1085 | + const uint16_t *src_y = (const void *)(d.in_data[0] + (d.in_linesize[0] * y)); |
| 1086 | + const uint16_t *src_cb = (const void *)(d.in_data[1] + (d.in_linesize[1] * y)); |
| 1087 | + const uint16_t *src_cr = (const void *)(d.in_data[2] + (d.in_linesize[2] * y)); |
| 1088 | + uint8_t *dst = d.out_data + (y * d.out_pitch); |
| 1089 | + |
| 1090 | + OPTIMIZED_FOR (int x = 0; x < width; ++x) { |
| 1091 | + comp_type_t cb = *src_cb++ - (1 << (S_DEPTH - 1)); |
| 1092 | + comp_type_t cr = *src_cr++ - (1 << (S_DEPTH - 1)); |
| 1093 | + comp_type_t y = |
| 1094 | + (*src_y++ - (1 << (S_DEPTH - 4))) * cfs.y_scale; |
| 1095 | + comp_type_t r = |
| 1096 | + YCBCR_TO_R(cfs, y, cb, cr) >> (COMP_BASE + 2); |
| 1097 | + comp_type_t g = |
| 1098 | + YCBCR_TO_G(cfs, y, cb, cr) >> (COMP_BASE + 2); |
| 1099 | + comp_type_t b = |
| 1100 | + YCBCR_TO_B(cfs, y, cb, cr) >> (COMP_BASE + 2); |
| 1101 | + if (out_rgba) { |
| 1102 | + *(uint32_t *)(void *) dst = MK_RGBA(r, g, b, alpha_mask, 8); |
| 1103 | + dst += 4; |
| 1104 | + } else { |
| 1105 | + *dst++ = CLAMP_FULL(r, 8); |
| 1106 | + *dst++ = CLAMP_FULL(g, 8); |
| 1107 | + *dst++ = CLAMP_FULL(b, 8); |
| 1108 | + } |
| 1109 | + } |
| 1110 | + } |
| 1111 | +} |
| 1112 | +void |
| 1113 | +yuv444p10le_to_rgb24(struct from_planar_data d) |
| 1114 | +{ |
| 1115 | + yuv444p10le_to_rgb(d, false); |
| 1116 | +} |
| 1117 | +void |
| 1118 | +yuv444p10le_to_rgb32(struct from_planar_data d) |
| 1119 | +{ |
| 1120 | + yuv444p10le_to_rgb(d, true); |
| 1121 | +} |
| 1122 | + |
0 commit comments