summaryrefslogtreecommitdiff
path: root/src/apps/qcam/format_converter.cpp
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2022-10-20 00:44:55 +0300
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2022-10-20 13:36:25 +0300
commit84ad104499d9efc0253dae1a60ee070ed375ad95 (patch)
treed10fd53eb79cebb28fa3f72b18b46dddb6382b83 /src/apps/qcam/format_converter.cpp
parentdaf3f4b59f4ea0ecb42c6a39fe909f071d3a2842 (diff)
Move test applications to src/apps/
The cam and qcam test application share code, currently through a crude hack that references the cam source files directly from the qcam meson.build file. To prepare for the introduction of hosting that code in a static library, move all applications to src/apps/. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Diffstat (limited to 'src/apps/qcam/format_converter.cpp')
-rw-r--r--src/apps/qcam/format_converter.cpp359
1 files changed, 359 insertions, 0 deletions
diff --git a/src/apps/qcam/format_converter.cpp b/src/apps/qcam/format_converter.cpp
new file mode 100644
index 00000000..9331da0c
--- /dev/null
+++ b/src/apps/qcam/format_converter.cpp
@@ -0,0 +1,359 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * format_convert.cpp - qcam - Convert buffer to RGB
+ */
+
+#include "format_converter.h"
+
+#include <errno.h>
+#include <utility>
+
+#include <QImage>
+
+#include <libcamera/formats.h>
+
+#include "../cam/image.h"
+
+#define RGBSHIFT 8
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#endif
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+#ifndef CLAMP
+#define CLAMP(a,low,high) MAX((low),MIN((high),(a)))
+#endif
+#ifndef CLIP
+#define CLIP(x) CLAMP(x,0,255)
+#endif
+
+int FormatConverter::configure(const libcamera::PixelFormat &format,
+ const QSize &size, unsigned int stride)
+{
+ switch (format) {
+ case libcamera::formats::NV12:
+ formatFamily_ = YUVSemiPlanar;
+ horzSubSample_ = 2;
+ vertSubSample_ = 2;
+ nvSwap_ = false;
+ break;
+ case libcamera::formats::NV21:
+ formatFamily_ = YUVSemiPlanar;
+ horzSubSample_ = 2;
+ vertSubSample_ = 2;
+ nvSwap_ = true;
+ break;
+ case libcamera::formats::NV16:
+ formatFamily_ = YUVSemiPlanar;
+ horzSubSample_ = 2;
+ vertSubSample_ = 1;
+ nvSwap_ = false;
+ break;
+ case libcamera::formats::NV61:
+ formatFamily_ = YUVSemiPlanar;
+ horzSubSample_ = 2;
+ vertSubSample_ = 1;
+ nvSwap_ = true;
+ break;
+ case libcamera::formats::NV24:
+ formatFamily_ = YUVSemiPlanar;
+ horzSubSample_ = 1;
+ vertSubSample_ = 1;
+ nvSwap_ = false;
+ break;
+ case libcamera::formats::NV42:
+ formatFamily_ = YUVSemiPlanar;
+ horzSubSample_ = 1;
+ vertSubSample_ = 1;
+ nvSwap_ = true;
+ break;
+
+ case libcamera::formats::R8:
+ formatFamily_ = RGB;
+ r_pos_ = 0;
+ g_pos_ = 0;
+ b_pos_ = 0;
+ bpp_ = 1;
+ break;
+ case libcamera::formats::RGB888:
+ formatFamily_ = RGB;
+ r_pos_ = 2;
+ g_pos_ = 1;
+ b_pos_ = 0;
+ bpp_ = 3;
+ break;
+ case libcamera::formats::BGR888:
+ formatFamily_ = RGB;
+ r_pos_ = 0;
+ g_pos_ = 1;
+ b_pos_ = 2;
+ bpp_ = 3;
+ break;
+ case libcamera::formats::ARGB8888:
+ case libcamera::formats::XRGB8888:
+ formatFamily_ = RGB;
+ r_pos_ = 2;
+ g_pos_ = 1;
+ b_pos_ = 0;
+ bpp_ = 4;
+ break;
+ case libcamera::formats::RGBA8888:
+ case libcamera::formats::RGBX8888:
+ formatFamily_ = RGB;
+ r_pos_ = 3;
+ g_pos_ = 2;
+ b_pos_ = 1;
+ bpp_ = 4;
+ break;
+ case libcamera::formats::ABGR8888:
+ case libcamera::formats::XBGR8888:
+ formatFamily_ = RGB;
+ r_pos_ = 0;
+ g_pos_ = 1;
+ b_pos_ = 2;
+ bpp_ = 4;
+ break;
+ case libcamera::formats::BGRA8888:
+ case libcamera::formats::BGRX8888:
+ formatFamily_ = RGB;
+ r_pos_ = 1;
+ g_pos_ = 2;
+ b_pos_ = 3;
+ bpp_ = 4;
+ break;
+
+ case libcamera::formats::VYUY:
+ formatFamily_ = YUVPacked;
+ y_pos_ = 1;
+ cb_pos_ = 2;
+ break;
+ case libcamera::formats::YVYU:
+ formatFamily_ = YUVPacked;
+ y_pos_ = 0;
+ cb_pos_ = 3;
+ break;
+ case libcamera::formats::UYVY:
+ formatFamily_ = YUVPacked;
+ y_pos_ = 1;
+ cb_pos_ = 0;
+ break;
+ case libcamera::formats::YUYV:
+ formatFamily_ = YUVPacked;
+ y_pos_ = 0;
+ cb_pos_ = 1;
+ break;
+
+ case libcamera::formats::YUV420:
+ formatFamily_ = YUVPlanar;
+ horzSubSample_ = 2;
+ vertSubSample_ = 2;
+ nvSwap_ = false;
+ break;
+ case libcamera::formats::YVU420:
+ formatFamily_ = YUVPlanar;
+ horzSubSample_ = 2;
+ vertSubSample_ = 2;
+ nvSwap_ = true;
+ break;
+ case libcamera::formats::YUV422:
+ formatFamily_ = YUVPlanar;
+ horzSubSample_ = 2;
+ vertSubSample_ = 1;
+ nvSwap_ = false;
+ break;
+
+ case libcamera::formats::MJPEG:
+ formatFamily_ = MJPEG;
+ break;
+
+ default:
+ return -EINVAL;
+ };
+
+ format_ = format;
+ width_ = size.width();
+ height_ = size.height();
+ stride_ = stride;
+
+ return 0;
+}
+
+void FormatConverter::convert(const Image *src, size_t size, QImage *dst)
+{
+ switch (formatFamily_) {
+ case MJPEG:
+ dst->loadFromData(src->data(0).data(), size, "JPEG");
+ break;
+ case RGB:
+ convertRGB(src, dst->bits());
+ break;
+ case YUVPacked:
+ convertYUVPacked(src, dst->bits());
+ break;
+ case YUVSemiPlanar:
+ convertYUVSemiPlanar(src, dst->bits());
+ break;
+ case YUVPlanar:
+ convertYUVPlanar(src, dst->bits());
+ break;
+ };
+}
+
+static void yuv_to_rgb(int y, int u, int v, int *r, int *g, int *b)
+{
+ int c = y - 16;
+ int d = u - 128;
+ int e = v - 128;
+ *r = CLIP(( 298 * c + 409 * e + 128) >> RGBSHIFT);
+ *g = CLIP(( 298 * c - 100 * d - 208 * e + 128) >> RGBSHIFT);
+ *b = CLIP(( 298 * c + 516 * d + 128) >> RGBSHIFT);
+}
+
+void FormatConverter::convertRGB(const Image *srcImage, unsigned char *dst)
+{
+ const unsigned char *src = srcImage->data(0).data();
+ unsigned int x, y;
+ int r, g, b;
+
+ for (y = 0; y < height_; y++) {
+ for (x = 0; x < width_; x++) {
+ r = src[bpp_ * x + r_pos_];
+ g = src[bpp_ * x + g_pos_];
+ b = src[bpp_ * x + b_pos_];
+
+ dst[4 * x + 0] = b;
+ dst[4 * x + 1] = g;
+ dst[4 * x + 2] = r;
+ dst[4 * x + 3] = 0xff;
+ }
+
+ src += stride_;
+ dst += width_ * 4;
+ }
+}
+
+void FormatConverter::convertYUVPacked(const Image *srcImage, unsigned char *dst)
+{
+ const unsigned char *src = srcImage->data(0).data();
+ unsigned int src_x, src_y, dst_x, dst_y;
+ unsigned int src_stride;
+ unsigned int dst_stride;
+ unsigned int cr_pos;
+ int r, g, b, y, cr, cb;
+
+ cr_pos = (cb_pos_ + 2) % 4;
+ src_stride = stride_;
+ dst_stride = width_ * 4;
+
+ for (src_y = 0, dst_y = 0; dst_y < height_; src_y++, dst_y++) {
+ for (src_x = 0, dst_x = 0; dst_x < width_; ) {
+ cb = src[src_y * src_stride + src_x * 4 + cb_pos_];
+ cr = src[src_y * src_stride + src_x * 4 + cr_pos];
+
+ y = src[src_y * src_stride + src_x * 4 + y_pos_];
+ yuv_to_rgb(y, cb, cr, &r, &g, &b);
+ dst[dst_y * dst_stride + 4 * dst_x + 0] = b;
+ dst[dst_y * dst_stride + 4 * dst_x + 1] = g;
+ dst[dst_y * dst_stride + 4 * dst_x + 2] = r;
+ dst[dst_y * dst_stride + 4 * dst_x + 3] = 0xff;
+ dst_x++;
+
+ y = src[src_y * src_stride + src_x * 4 + y_pos_ + 2];
+ yuv_to_rgb(y, cb, cr, &r, &g, &b);
+ dst[dst_y * dst_stride + 4 * dst_x + 0] = b;
+ dst[dst_y * dst_stride + 4 * dst_x + 1] = g;
+ dst[dst_y * dst_stride + 4 * dst_x + 2] = r;
+ dst[dst_y * dst_stride + 4 * dst_x + 3] = 0xff;
+ dst_x++;
+
+ src_x++;
+ }
+ }
+}
+
+void FormatConverter::convertYUVPlanar(const Image *srcImage, unsigned char *dst)
+{
+ unsigned int c_stride = stride_ / horzSubSample_;
+ unsigned int c_inc = horzSubSample_ == 1 ? 1 : 0;
+ const unsigned char *src_y = srcImage->data(0).data();
+ const unsigned char *src_cb = srcImage->data(1).data();
+ const unsigned char *src_cr = srcImage->data(2).data();
+ int r, g, b;
+
+ if (nvSwap_)
+ std::swap(src_cb, src_cr);
+
+ for (unsigned int y = 0; y < height_; y++) {
+ const unsigned char *line_y = src_y + y * stride_;
+ const unsigned char *line_cb = src_cb + (y / vertSubSample_) *
+ c_stride;
+ const unsigned char *line_cr = src_cr + (y / vertSubSample_) *
+ c_stride;
+
+ for (unsigned int x = 0; x < width_; x += 2) {
+ yuv_to_rgb(*line_y, *line_cb, *line_cr, &r, &g, &b);
+ dst[0] = b;
+ dst[1] = g;
+ dst[2] = r;
+ dst[3] = 0xff;
+ line_y++;
+ line_cb += c_inc;
+ line_cr += c_inc;
+ dst += 4;
+
+ yuv_to_rgb(*line_y, *line_cb, *line_cr, &r, &g, &b);
+ dst[0] = b;
+ dst[1] = g;
+ dst[2] = r;
+ dst[3] = 0xff;
+ line_y++;
+ line_cb += 1;
+ line_cr += 1;
+ dst += 4;
+ }
+ }
+}
+
+void FormatConverter::convertYUVSemiPlanar(const Image *srcImage, unsigned char *dst)
+{
+ unsigned int c_stride = stride_ * (2 / horzSubSample_);
+ unsigned int c_inc = horzSubSample_ == 1 ? 2 : 0;
+ unsigned int cb_pos = nvSwap_ ? 1 : 0;
+ unsigned int cr_pos = nvSwap_ ? 0 : 1;
+ const unsigned char *src = srcImage->data(0).data();
+ const unsigned char *src_c = srcImage->data(1).data();
+ int r, g, b;
+
+ for (unsigned int y = 0; y < height_; y++) {
+ const unsigned char *src_y = src + y * stride_;
+ const unsigned char *src_cb = src_c + (y / vertSubSample_) *
+ c_stride + cb_pos;
+ const unsigned char *src_cr = src_c + (y / vertSubSample_) *
+ c_stride + cr_pos;
+
+ for (unsigned int x = 0; x < width_; x += 2) {
+ yuv_to_rgb(*src_y, *src_cb, *src_cr, &r, &g, &b);
+ dst[0] = b;
+ dst[1] = g;
+ dst[2] = r;
+ dst[3] = 0xff;
+ src_y++;
+ src_cb += c_inc;
+ src_cr += c_inc;
+ dst += 4;
+
+ yuv_to_rgb(*src_y, *src_cb, *src_cr, &r, &g, &b);
+ dst[0] = b;
+ dst[1] = g;
+ dst[2] = r;
+ dst[3] = 0xff;
+ src_y++;
+ src_cb += 2;
+ src_cr += 2;
+ dst += 4;
+ }
+ }
+}