@@ -979,6 +979,101 @@ static void image_convert_rgb_to_hsv(const struct image *input,
}
}
+/* YUV ro RGB */
+static void csc_yuv_to_rgb(const int ccm[3][3], bool full_range,
+ const uint8_t ycbcr[3], uint8_t rgb[3])
+{
+ int y_min = full_range ? 0 : 16;
+ int div = 1 << 11;
+ int y, cb, cr;
+ int r, g, b;
+
+ y = ycbcr[0] - y_min;
+ cb = ycbcr[1] - 128;
+ cr = ycbcr[2] - 128;
+
+ r = (ccm[0][0] * y + ccm[0][1] * cb + ccm[0][2] * cr + div / 2) / div;
+ g = (ccm[1][0] * y + ccm[1][1] * cb + ccm[1][2] * cr + div / 2) / div;
+ b = (ccm[2][0] * y + ccm[2][1] * cb + ccm[2][2] * cr + div / 2) / div;
+
+ rgb[0] = CLAMP(r, 0, 255);
+ rgb[1] = CLAMP(g, 0, 255);
+ rgb[2] = CLAMP(b, 0, 255);
+}
+
+static void image_convert_yuv_to_rgb(const struct image *input,
+ struct image *output,
+ const struct format_info *format,
+ const struct csc_params *params)
+{
+ /*
+ * The value of the coefficients has been reverse-engineered by
+ * analyzing the VSP1 RGB output values for carefully crafted input YUV
+ * data. The hardware precision of the coefficients appears to be Q1.11.
+ *
+ * The exact way to derive those fixed-point coefficients from the
+ * BT.601 and BT.709 standard values is not know, none of the tested
+ * rounding methods (rounding down, rounding up, rounding to the closest
+ * integer, or rounding to minimum the error on the sum of each line)
+ * produce the fixed-point values used by the hardware.
+ *
+ * While the coefficients for BT.601 in both ranges, and BT.709 in
+ * limited range, differ from the values listed in the respective
+ * standards by at most a single unit, some of the BT.709 full range
+ * coefficients differ more significantly. The first column of the
+ * matrix matches the standard, but the second and third columns seem to
+ * be divided by a factor equal to (240-16)/(235-16). The reason is not
+ * currently understood, but the value of the factor strongly hints that
+ * this isn't a random difference.
+ */
+ static const int bt601[3][3] = {
+ { 2384, 0, 3269 },
+ { 2384, -803, -1665 },
+ { 2384, 4131, 0 },
+ };
+ static const int bt601_full[3][3] = {
+ { 2048, 0, 2871 },
+ { 2048, -705, -1463 },
+ { 2048, 3629, 0 },
+ };
+ static const int rec709[3][3] = {
+ { 2385, 0, 3672 },
+ { 2385, -437, -1092 },
+ { 2385, 4326, 0 },
+ };
+ static const int rec709_full[3][3] = {
+ { 2048, 0, 3153 },
+ { 2048, -375, -937 },
+ { 2048, 3715, 0 },
+ };
+
+ bool full_range = params->quantization == V4L2_QUANTIZATION_FULL_RANGE;
+ const int (*matrix)[3][3];
+ const uint8_t *idata = input->data;
+ uint8_t *odata = output->data;
+ unsigned int x;
+ unsigned int y;
+
+ switch (params->encoding) {
+ case V4L2_YCBCR_ENC_601:
+ default:
+ matrix = full_range ? &bt601_full : &bt601;
+ break;
+ case V4L2_YCBCR_ENC_709:
+ matrix = full_range ? &rec709_full : &rec709;
+ break;
+ }
+
+ for (y = 0; y < output->height; ++y) {
+ for (x = 0; x < output->width; ++x)
+ csc_yuv_to_rgb(*matrix, full_range,
+ &idata[3*x], &odata[3*x]);
+
+ idata += 3 * output->width;
+ odata += 3 * output->width;
+ }
+}
+
typedef void (*image_convert_func)(const struct image *input,
struct image *output,
const struct format_info *format,
@@ -994,6 +1089,7 @@ static const struct image_converter image_converters[] = {
{ FORMAT_RGB, FORMAT_HSV, image_convert_rgb_to_hsv },
{ FORMAT_RGB, FORMAT_RGB, image_convert_rgb_to_rgb },
{ FORMAT_RGB, FORMAT_YUV, image_convert_rgb_to_yuv },
+ { FORMAT_YUV, FORMAT_RGB, image_convert_yuv_to_rgb },
};
static int image_convert(struct image *input, const struct format_info *format,
Support YUV to RGB conversion, to emulate the RPF and WPF CSC. As for RGB to YUV conversion, the coefficients have been reverse-engineered. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> --- src/gen-image.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+)