[libcamera-devel,v2,4/5] qcam: format_converter: Add fully-planar YUV formats support
diff mbox series

Message ID 20210907002044.7319-5-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • qcam: Fix stride handling
Related show

Commit Message

Laurent Pinchart Sept. 7, 2021, 12:20 a.m. UTC
Add support for the YUV420, YVU420 and YUV420 formats supported by
libcamera. YUV420 can be produced by the Raspberry Pi pipeline handler,
being able to display it is useful for testing.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 src/qcam/format_converter.cpp | 66 +++++++++++++++++++++++++++++++++++
 src/qcam/format_converter.h   |  2 ++
 2 files changed, 68 insertions(+)

Comments

Kieran Bingham Sept. 21, 2021, 3:35 p.m. UTC | #1
On 07/09/2021 01:20, Laurent Pinchart wrote:
> Add support for the YUV420, YVU420 and YUV420 formats supported by
> libcamera. YUV420 can be produced by the Raspberry Pi pipeline handler,
> being able to display it is useful for testing.
> 

These need adding to the gstlibcamera-utils.cpp format_map too.
I've done that while working through to verify the formats here.

Patch incoming separately.

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>


> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  src/qcam/format_converter.cpp | 66 +++++++++++++++++++++++++++++++++++
>  src/qcam/format_converter.h   |  2 ++
>  2 files changed, 68 insertions(+)
> 
> diff --git a/src/qcam/format_converter.cpp b/src/qcam/format_converter.cpp
> index 7979ea8a77ff..d4d3223bc698 100644
> --- a/src/qcam/format_converter.cpp
> +++ b/src/qcam/format_converter.cpp
> @@ -8,6 +8,7 @@
>  #include "format_converter.h"
>  
>  #include <errno.h>
> +#include <utility>
>  
>  #include <QImage>
>  
> @@ -141,6 +142,25 @@ int FormatConverter::configure(const libcamera::PixelFormat &format,
>  		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;
> @@ -172,6 +192,9 @@ void FormatConverter::convert(const Image *src, size_t size, QImage *dst)
>  	case YUVSemiPlanar:
>  		convertYUVSemiPlanar(src, dst->bits());
>  		break;
> +	case YUVPlanar:
> +		convertYUVPlanar(src, dst->bits());
> +		break;
>  	};
>  }
>  
> @@ -247,6 +270,49 @@ void FormatConverter::convertYUVPacked(const Image *srcImage, unsigned char *dst
>  	}
>  }
>  
> +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_);
> diff --git a/src/qcam/format_converter.h b/src/qcam/format_converter.h
> index 9ce2cc6da7f2..9da2df5d294b 100644
> --- a/src/qcam/format_converter.h
> +++ b/src/qcam/format_converter.h
> @@ -29,11 +29,13 @@ private:
>  		MJPEG,
>  		RGB,
>  		YUVPacked,
> +		YUVPlanar,
>  		YUVSemiPlanar,
>  	};
>  
>  	void convertRGB(const Image *src, unsigned char *dst);
>  	void convertYUVPacked(const Image *src, unsigned char *dst);
> +	void convertYUVPlanar(const Image *src, unsigned char *dst);
>  	void convertYUVSemiPlanar(const Image *src, unsigned char *dst);
>  
>  	libcamera::PixelFormat format_;
>
Paul Elder Sept. 22, 2021, 7:09 a.m. UTC | #2
Hi Laurent,

On Tue, Sep 07, 2021 at 03:20:43AM +0300, Laurent Pinchart wrote:
> Add support for the YUV420, YVU420 and YUV420 formats supported by

YUV420 and YUV420? :p

> libcamera. YUV420 can be produced by the Raspberry Pi pipeline handler,
> being able to display it is useful for testing.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>

> ---
>  src/qcam/format_converter.cpp | 66 +++++++++++++++++++++++++++++++++++
>  src/qcam/format_converter.h   |  2 ++
>  2 files changed, 68 insertions(+)
> 
> diff --git a/src/qcam/format_converter.cpp b/src/qcam/format_converter.cpp
> index 7979ea8a77ff..d4d3223bc698 100644
> --- a/src/qcam/format_converter.cpp
> +++ b/src/qcam/format_converter.cpp
> @@ -8,6 +8,7 @@
>  #include "format_converter.h"
>  
>  #include <errno.h>
> +#include <utility>
>  
>  #include <QImage>
>  
> @@ -141,6 +142,25 @@ int FormatConverter::configure(const libcamera::PixelFormat &format,
>  		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;
> @@ -172,6 +192,9 @@ void FormatConverter::convert(const Image *src, size_t size, QImage *dst)
>  	case YUVSemiPlanar:
>  		convertYUVSemiPlanar(src, dst->bits());
>  		break;
> +	case YUVPlanar:
> +		convertYUVPlanar(src, dst->bits());
> +		break;
>  	};
>  }
>  
> @@ -247,6 +270,49 @@ void FormatConverter::convertYUVPacked(const Image *srcImage, unsigned char *dst
>  	}
>  }
>  
> +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_);
> diff --git a/src/qcam/format_converter.h b/src/qcam/format_converter.h
> index 9ce2cc6da7f2..9da2df5d294b 100644
> --- a/src/qcam/format_converter.h
> +++ b/src/qcam/format_converter.h
> @@ -29,11 +29,13 @@ private:
>  		MJPEG,
>  		RGB,
>  		YUVPacked,
> +		YUVPlanar,
>  		YUVSemiPlanar,
>  	};
>  
>  	void convertRGB(const Image *src, unsigned char *dst);
>  	void convertYUVPacked(const Image *src, unsigned char *dst);
> +	void convertYUVPlanar(const Image *src, unsigned char *dst);
>  	void convertYUVSemiPlanar(const Image *src, unsigned char *dst);
>  
>  	libcamera::PixelFormat format_;
> -- 
> Regards,
> 
> Laurent Pinchart
>
Laurent Pinchart Sept. 22, 2021, 7:54 a.m. UTC | #3
Hi Paul,

On Wed, Sep 22, 2021 at 04:09:19PM +0900, paul.elder@ideasonboard.com wrote:
> On Tue, Sep 07, 2021 at 03:20:43AM +0300, Laurent Pinchart wrote:
> > Add support for the YUV420, YVU420 and YUV420 formats supported by
> 
> YUV420 and YUV420? :p

Oops :-) I'll fix that.

> > libcamera. YUV420 can be produced by the Raspberry Pi pipeline handler,
> > being able to display it is useful for testing.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
> 
> > ---
> >  src/qcam/format_converter.cpp | 66 +++++++++++++++++++++++++++++++++++
> >  src/qcam/format_converter.h   |  2 ++
> >  2 files changed, 68 insertions(+)
> > 
> > diff --git a/src/qcam/format_converter.cpp b/src/qcam/format_converter.cpp
> > index 7979ea8a77ff..d4d3223bc698 100644
> > --- a/src/qcam/format_converter.cpp
> > +++ b/src/qcam/format_converter.cpp
> > @@ -8,6 +8,7 @@
> >  #include "format_converter.h"
> >  
> >  #include <errno.h>
> > +#include <utility>
> >  
> >  #include <QImage>
> >  
> > @@ -141,6 +142,25 @@ int FormatConverter::configure(const libcamera::PixelFormat &format,
> >  		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;
> > @@ -172,6 +192,9 @@ void FormatConverter::convert(const Image *src, size_t size, QImage *dst)
> >  	case YUVSemiPlanar:
> >  		convertYUVSemiPlanar(src, dst->bits());
> >  		break;
> > +	case YUVPlanar:
> > +		convertYUVPlanar(src, dst->bits());
> > +		break;
> >  	};
> >  }
> >  
> > @@ -247,6 +270,49 @@ void FormatConverter::convertYUVPacked(const Image *srcImage, unsigned char *dst
> >  	}
> >  }
> >  
> > +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_);
> > diff --git a/src/qcam/format_converter.h b/src/qcam/format_converter.h
> > index 9ce2cc6da7f2..9da2df5d294b 100644
> > --- a/src/qcam/format_converter.h
> > +++ b/src/qcam/format_converter.h
> > @@ -29,11 +29,13 @@ private:
> >  		MJPEG,
> >  		RGB,
> >  		YUVPacked,
> > +		YUVPlanar,
> >  		YUVSemiPlanar,
> >  	};
> >  
> >  	void convertRGB(const Image *src, unsigned char *dst);
> >  	void convertYUVPacked(const Image *src, unsigned char *dst);
> > +	void convertYUVPlanar(const Image *src, unsigned char *dst);
> >  	void convertYUVSemiPlanar(const Image *src, unsigned char *dst);
> >  
> >  	libcamera::PixelFormat format_;

Patch
diff mbox series

diff --git a/src/qcam/format_converter.cpp b/src/qcam/format_converter.cpp
index 7979ea8a77ff..d4d3223bc698 100644
--- a/src/qcam/format_converter.cpp
+++ b/src/qcam/format_converter.cpp
@@ -8,6 +8,7 @@ 
 #include "format_converter.h"
 
 #include <errno.h>
+#include <utility>
 
 #include <QImage>
 
@@ -141,6 +142,25 @@  int FormatConverter::configure(const libcamera::PixelFormat &format,
 		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;
@@ -172,6 +192,9 @@  void FormatConverter::convert(const Image *src, size_t size, QImage *dst)
 	case YUVSemiPlanar:
 		convertYUVSemiPlanar(src, dst->bits());
 		break;
+	case YUVPlanar:
+		convertYUVPlanar(src, dst->bits());
+		break;
 	};
 }
 
@@ -247,6 +270,49 @@  void FormatConverter::convertYUVPacked(const Image *srcImage, unsigned char *dst
 	}
 }
 
+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_);
diff --git a/src/qcam/format_converter.h b/src/qcam/format_converter.h
index 9ce2cc6da7f2..9da2df5d294b 100644
--- a/src/qcam/format_converter.h
+++ b/src/qcam/format_converter.h
@@ -29,11 +29,13 @@  private:
 		MJPEG,
 		RGB,
 		YUVPacked,
+		YUVPlanar,
 		YUVSemiPlanar,
 	};
 
 	void convertRGB(const Image *src, unsigned char *dst);
 	void convertYUVPacked(const Image *src, unsigned char *dst);
+	void convertYUVPlanar(const Image *src, unsigned char *dst);
 	void convertYUVSemiPlanar(const Image *src, unsigned char *dst);
 
 	libcamera::PixelFormat format_;