From patchwork Tue Sep 7 00:20:40 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 13704 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id C9A2FBDC71 for ; Tue, 7 Sep 2021 00:21:13 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C0AF069178; Tue, 7 Sep 2021 02:21:10 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="mYu+n1A1"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A563069167 for ; Tue, 7 Sep 2021 02:21:07 +0200 (CEST) Received: from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3D124993 for ; Tue, 7 Sep 2021 02:21:07 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1630974067; bh=62WmNlGgrpwlRornY2x0Y+b25WlZ4o246XoUY+EFKw8=; h=From:To:Subject:Date:In-Reply-To:References:From; b=mYu+n1A1JAY3mRAo1F/l/sVbLLrQ6UhjiDODIOKxbEcUewkP2hTAIDk7938E1E0SN 2P9XznVbNizlzwscKJtUYxfA5o6uDHh9oZaMhyK8RwcPB/9IgPnDTTy64bb1Wz8gKK R9Q5S8GcTUXFBgSSvKG43YJ19MzhPw5VNnnzJXYU= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 7 Sep 2021 03:20:40 +0300 Message-Id: <20210907002044.7319-2-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210907002044.7319-1-laurent.pinchart@ideasonboard.com> References: <20210907002044.7319-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 1/5] qcam: viewfinder: Pass stride value to viewfinder X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" qcam currently assumes that no padding is used at end of lines, and uses the image width as the stride. This leads to rendering failures with some formats on some platforms. To prepare for stride support, add a stride parameter to the ViewFinder::setFormat() function to pass the stride from the stream configuration to the viewfinder. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham Reviewed-by: Paul Elder --- src/qcam/main_window.cpp | 3 ++- src/qcam/viewfinder.h | 3 ++- src/qcam/viewfinder_gl.cpp | 7 ++----- src/qcam/viewfinder_gl.h | 3 ++- src/qcam/viewfinder_qt.cpp | 3 ++- src/qcam/viewfinder_qt.h | 3 ++- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index 168dd5ce30e3..bb6b03993add 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -448,7 +448,8 @@ int MainWindow::startCapture() /* Configure the viewfinder. */ ret = viewfinder_->setFormat(vfConfig.pixelFormat, - QSize(vfConfig.size.width, vfConfig.size.height)); + QSize(vfConfig.size.width, vfConfig.size.height), + vfConfig.stride); if (ret < 0) { qInfo() << "Failed to set viewfinder format"; return ret; diff --git a/src/qcam/viewfinder.h b/src/qcam/viewfinder.h index fb462835fb5f..4c2102a6ed04 100644 --- a/src/qcam/viewfinder.h +++ b/src/qcam/viewfinder.h @@ -23,7 +23,8 @@ public: virtual const QList &nativeFormats() const = 0; - virtual int setFormat(const libcamera::PixelFormat &format, const QSize &size) = 0; + virtual int setFormat(const libcamera::PixelFormat &format, const QSize &size, + unsigned int stride) = 0; virtual void render(libcamera::FrameBuffer *buffer, Image *image) = 0; virtual void stop() = 0; diff --git a/src/qcam/viewfinder_gl.cpp b/src/qcam/viewfinder_gl.cpp index 32232faa2ad8..aeb1ea02d2d5 100644 --- a/src/qcam/viewfinder_gl.cpp +++ b/src/qcam/viewfinder_gl.cpp @@ -72,7 +72,7 @@ const QList &ViewFinderGL::nativeFormats() const } int ViewFinderGL::setFormat(const libcamera::PixelFormat &format, - const QSize &size) + const QSize &size, unsigned int stride) { if (format != format_) { /* @@ -92,6 +92,7 @@ int ViewFinderGL::setFormat(const libcamera::PixelFormat &format, } size_ = size; + stride_ = stride; updateGeometry(); return 0; @@ -119,10 +120,6 @@ void ViewFinderGL::render(libcamera::FrameBuffer *buffer, Image *image) renderComplete(buffer_); image_ = image; - /* - * \todo Get the stride from the buffer instead of computing it naively - */ - stride_ = buffer->metadata().planes()[0].bytesused / size_.height(); update(); buffer_ = buffer; } diff --git a/src/qcam/viewfinder_gl.h b/src/qcam/viewfinder_gl.h index 72a60ecb9159..2b2b1e86035a 100644 --- a/src/qcam/viewfinder_gl.h +++ b/src/qcam/viewfinder_gl.h @@ -38,7 +38,8 @@ public: const QList &nativeFormats() const override; - int setFormat(const libcamera::PixelFormat &format, const QSize &size) override; + int setFormat(const libcamera::PixelFormat &format, const QSize &size, + unsigned int stride) override; void render(libcamera::FrameBuffer *buffer, Image *image) override; void stop() override; diff --git a/src/qcam/viewfinder_qt.cpp b/src/qcam/viewfinder_qt.cpp index 0d357d860014..cd051760160c 100644 --- a/src/qcam/viewfinder_qt.cpp +++ b/src/qcam/viewfinder_qt.cpp @@ -52,7 +52,8 @@ const QList &ViewFinderQt::nativeFormats() const } int ViewFinderQt::setFormat(const libcamera::PixelFormat &format, - const QSize &size) + const QSize &size, + [[maybe_unused]] unsigned int stride) { image_ = QImage(); diff --git a/src/qcam/viewfinder_qt.h b/src/qcam/viewfinder_qt.h index 6b48ef48a7d1..756f3fa33055 100644 --- a/src/qcam/viewfinder_qt.h +++ b/src/qcam/viewfinder_qt.h @@ -31,7 +31,8 @@ public: const QList &nativeFormats() const override; - int setFormat(const libcamera::PixelFormat &format, const QSize &size) override; + int setFormat(const libcamera::PixelFormat &format, const QSize &size, + unsigned int stride) override; void render(libcamera::FrameBuffer *buffer, Image *image) override; void stop() override; From patchwork Tue Sep 7 00:20:41 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 13705 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 32D9EBDC71 for ; Tue, 7 Sep 2021 00:21:15 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7DDB66916C; Tue, 7 Sep 2021 02:21:11 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="IUgHo/S9"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E5C1A69168 for ; Tue, 7 Sep 2021 02:21:07 +0200 (CEST) Received: from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8A2BA891 for ; Tue, 7 Sep 2021 02:21:07 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1630974067; bh=yI7FDPFuliZmBZ0WCHAxF7tjPfiMMucGxtT5aeV7GT8=; h=From:To:Subject:Date:In-Reply-To:References:From; b=IUgHo/S9DemX7LS7VmQ5TFmkKRLTYeeogQlcwmaAhu7zfQhAYiflKZt6gM7BgFWp2 mxenJVqwRuorw/N6IeEuOoLKUeMjYhWw9flM4EZIC3ffy7wcC6w5GPcVfG4GtlXJP1 cXnVDmpSmxPM1E3F94JGX8eB/He9/sUs/nLwhtiU= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 7 Sep 2021 03:20:41 +0300 Message-Id: <20210907002044.7319-3-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210907002044.7319-1-laurent.pinchart@ideasonboard.com> References: <20210907002044.7319-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 2/5] qcam: format_converter: Add configurable stride support X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Make the stride configurable to support convertion of images with padding at the end of lines. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham Reviewed-by: Paul Elder --- Changes since v1: - Fix bad rebase --- src/qcam/format_converter.cpp | 11 ++++++----- src/qcam/format_converter.h | 4 +++- src/qcam/viewfinder_qt.cpp | 5 ++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/qcam/format_converter.cpp b/src/qcam/format_converter.cpp index 673ad33e141d..b4c7b0ca0edf 100644 --- a/src/qcam/format_converter.cpp +++ b/src/qcam/format_converter.cpp @@ -30,7 +30,7 @@ #endif int FormatConverter::configure(const libcamera::PixelFormat &format, - const QSize &size) + const QSize &size, unsigned int stride) { switch (format) { case libcamera::formats::NV12: @@ -152,6 +152,7 @@ int FormatConverter::configure(const libcamera::PixelFormat &format, format_ = format; width_ = size.width(); height_ = size.height(); + stride_ = stride; return 0; } @@ -186,7 +187,7 @@ static void yuv_to_rgb(int y, int u, int v, int *r, int *g, int *b) void FormatConverter::convertNV(const Image *srcImage, unsigned char *dst) { - unsigned int c_stride = width_ * (2 / horzSubSample_); + 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; @@ -195,7 +196,7 @@ void FormatConverter::convertNV(const Image *srcImage, unsigned char *dst) int r, g, b; for (unsigned int y = 0; y < height_; y++) { - const unsigned char *src_y = src + y * width_; + 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_) * @@ -243,7 +244,7 @@ void FormatConverter::convertRGB(const Image *srcImage, unsigned char *dst) dst[4 * x + 3] = 0xff; } - src += width_ * bpp_; + src += stride_; dst += width_ * 4; } } @@ -258,7 +259,7 @@ void FormatConverter::convertYUV(const Image *srcImage, unsigned char *dst) int r, g, b, y, cr, cb; cr_pos = (cb_pos_ + 2) % 4; - src_stride = width_ * 2; + src_stride = stride_; dst_stride = width_ * 4; for (src_y = 0, dst_y = 0; dst_y < height_; src_y++, dst_y++) { diff --git a/src/qcam/format_converter.h b/src/qcam/format_converter.h index 2220a62b5f11..bb04aa959a1a 100644 --- a/src/qcam/format_converter.h +++ b/src/qcam/format_converter.h @@ -19,7 +19,8 @@ class QImage; class FormatConverter { public: - int configure(const libcamera::PixelFormat &format, const QSize &size); + int configure(const libcamera::PixelFormat &format, const QSize &size, + unsigned int stride); void convert(const Image *src, size_t size, QImage *dst); @@ -38,6 +39,7 @@ private: libcamera::PixelFormat format_; unsigned int width_; unsigned int height_; + unsigned int stride_; enum FormatFamily formatFamily_; diff --git a/src/qcam/viewfinder_qt.cpp b/src/qcam/viewfinder_qt.cpp index cd051760160c..a05c75ed9e12 100644 --- a/src/qcam/viewfinder_qt.cpp +++ b/src/qcam/viewfinder_qt.cpp @@ -52,8 +52,7 @@ const QList &ViewFinderQt::nativeFormats() const } int ViewFinderQt::setFormat(const libcamera::PixelFormat &format, - const QSize &size, - [[maybe_unused]] unsigned int stride) + const QSize &size, unsigned int stride) { image_ = QImage(); @@ -62,7 +61,7 @@ int ViewFinderQt::setFormat(const libcamera::PixelFormat &format, * the destination image. */ if (!::nativeFormats.contains(format)) { - int ret = converter_.configure(format, size); + int ret = converter_.configure(format, size, stride); if (ret < 0) return ret; From patchwork Tue Sep 7 00:20:42 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 13706 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id DE15FBDC71 for ; Tue, 7 Sep 2021 00:21:15 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F2A246917C; Tue, 7 Sep 2021 02:21:11 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="KqoRnepS"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3E39F6916A for ; Tue, 7 Sep 2021 02:21:08 +0200 (CEST) Received: from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D7DF9993 for ; Tue, 7 Sep 2021 02:21:07 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1630974068; bh=EG9+kCAtUs+CzAxar++/juJo7WzLafy5Omntq+M/WSo=; h=From:To:Subject:Date:In-Reply-To:References:From; b=KqoRnepSMqVv4TPc31e7XIkADvjhGBbUW9NvW5sp2/DeYAFu3/NvQ9VRcleEMJBrL qU2aUC01UE+0+kKs23AoIC26OBMKsYvVovN3+sV/Q1pc22q4FF3r+5ExVVC0WA2ctR LR5upN6SecC6HrETsigg4IZUGk8TsfHQab9wh+pw= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 7 Sep 2021 03:20:42 +0300 Message-Id: <20210907002044.7319-4-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210907002044.7319-1-laurent.pinchart@ideasonboard.com> References: <20210907002044.7319-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 3/5] qcam: format_converter: Rename YUV and NV to YUVPacked and YUVSemiPlanar X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" To prepare for fully-planar YUV support, rename the existing format families YUV and NV to YUVPacked and YUVSemiPlanar respectively. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham Reviewed-by: Paul Elder --- src/qcam/format_converter.cpp | 114 +++++++++++++++++----------------- src/qcam/format_converter.h | 8 +-- 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/src/qcam/format_converter.cpp b/src/qcam/format_converter.cpp index b4c7b0ca0edf..7979ea8a77ff 100644 --- a/src/qcam/format_converter.cpp +++ b/src/qcam/format_converter.cpp @@ -34,37 +34,37 @@ int FormatConverter::configure(const libcamera::PixelFormat &format, { switch (format) { case libcamera::formats::NV12: - formatFamily_ = NV; + formatFamily_ = YUVSemiPlanar; horzSubSample_ = 2; vertSubSample_ = 2; nvSwap_ = false; break; case libcamera::formats::NV21: - formatFamily_ = NV; + formatFamily_ = YUVSemiPlanar; horzSubSample_ = 2; vertSubSample_ = 2; nvSwap_ = true; break; case libcamera::formats::NV16: - formatFamily_ = NV; + formatFamily_ = YUVSemiPlanar; horzSubSample_ = 2; vertSubSample_ = 1; nvSwap_ = false; break; case libcamera::formats::NV61: - formatFamily_ = NV; + formatFamily_ = YUVSemiPlanar; horzSubSample_ = 2; vertSubSample_ = 1; nvSwap_ = true; break; case libcamera::formats::NV24: - formatFamily_ = NV; + formatFamily_ = YUVSemiPlanar; horzSubSample_ = 1; vertSubSample_ = 1; nvSwap_ = false; break; case libcamera::formats::NV42: - formatFamily_ = NV; + formatFamily_ = YUVSemiPlanar; horzSubSample_ = 1; vertSubSample_ = 1; nvSwap_ = true; @@ -121,22 +121,22 @@ int FormatConverter::configure(const libcamera::PixelFormat &format, break; case libcamera::formats::VYUY: - formatFamily_ = YUV; + formatFamily_ = YUVPacked; y_pos_ = 1; cb_pos_ = 2; break; case libcamera::formats::YVYU: - formatFamily_ = YUV; + formatFamily_ = YUVPacked; y_pos_ = 0; cb_pos_ = 3; break; case libcamera::formats::UYVY: - formatFamily_ = YUV; + formatFamily_ = YUVPacked; y_pos_ = 1; cb_pos_ = 0; break; case libcamera::formats::YUYV: - formatFamily_ = YUV; + formatFamily_ = YUVPacked; y_pos_ = 0; cb_pos_ = 1; break; @@ -163,14 +163,14 @@ void FormatConverter::convert(const Image *src, size_t size, QImage *dst) case MJPEG: dst->loadFromData(src->data(0).data(), size, "JPEG"); break; - case YUV: - convertYUV(src, dst->bits()); - break; case RGB: convertRGB(src, dst->bits()); break; - case NV: - convertNV(src, dst->bits()); + case YUVPacked: + convertYUVPacked(src, dst->bits()); + break; + case YUVSemiPlanar: + convertYUVSemiPlanar(src, dst->bits()); break; }; } @@ -185,47 +185,6 @@ static void yuv_to_rgb(int y, int u, int v, int *r, int *g, int *b) *b = CLIP(( 298 * c + 516 * d + 128) >> RGBSHIFT); } -void FormatConverter::convertNV(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; - } - } -} - void FormatConverter::convertRGB(const Image *srcImage, unsigned char *dst) { const unsigned char *src = srcImage->data(0).data(); @@ -249,7 +208,7 @@ void FormatConverter::convertRGB(const Image *srcImage, unsigned char *dst) } } -void FormatConverter::convertYUV(const Image *srcImage, unsigned char *dst) +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; @@ -287,3 +246,44 @@ void FormatConverter::convertYUV(const Image *srcImage, unsigned char *dst) } } } + +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; + } + } +} diff --git a/src/qcam/format_converter.h b/src/qcam/format_converter.h index bb04aa959a1a..9ce2cc6da7f2 100644 --- a/src/qcam/format_converter.h +++ b/src/qcam/format_converter.h @@ -27,14 +27,14 @@ public: private: enum FormatFamily { MJPEG, - NV, RGB, - YUV, + YUVPacked, + YUVSemiPlanar, }; - void convertNV(const Image *src, unsigned char *dst); void convertRGB(const Image *src, unsigned char *dst); - void convertYUV(const Image *src, unsigned char *dst); + void convertYUVPacked(const Image *src, unsigned char *dst); + void convertYUVSemiPlanar(const Image *src, unsigned char *dst); libcamera::PixelFormat format_; unsigned int width_; From patchwork Tue Sep 7 00:20:43 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 13707 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 57B91BDC71 for ; Tue, 7 Sep 2021 00:21:16 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 77E7369171; Tue, 7 Sep 2021 02:21:12 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="kAWTBHdL"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 934C56916B for ; Tue, 7 Sep 2021 02:21:08 +0200 (CEST) Received: from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 35BB0891 for ; Tue, 7 Sep 2021 02:21:08 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1630974068; bh=SnYvx102aSYO0JZckMaGntaQZxQtSYhiDQYwOxKYTyE=; h=From:To:Subject:Date:In-Reply-To:References:From; b=kAWTBHdLN4mo4hT4DqxWhMOmBzmaqnefGXLA260OCZ4mJSg09iJSZD2IOx2+2pxSf DqQ3rihMY9S8sazVaq4rAhl+CahvgWETjLmMwdkH7PfEPoCFsA0bsgmBmUmb6ISDcP gMjfrekB+pGnhFYHA04qmO5NSi6PLSQ66mxv1gEI= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 7 Sep 2021 03:20:43 +0300 Message-Id: <20210907002044.7319-5-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210907002044.7319-1-laurent.pinchart@ideasonboard.com> References: <20210907002044.7319-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 4/5] qcam: format_converter: Add fully-planar YUV formats support X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 Reviewed-by: Kieran Bingham Reviewed-by: Paul Elder --- 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 +#include #include @@ -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_; From patchwork Tue Sep 7 00:20:44 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 13708 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id A2018BEFBE for ; Tue, 7 Sep 2021 00:21:16 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1210469180; Tue, 7 Sep 2021 02:21:13 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="o+YsCFwZ"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DC62260253 for ; Tue, 7 Sep 2021 02:21:08 +0200 (CEST) Received: from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 873C1993 for ; Tue, 7 Sep 2021 02:21:08 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1630974068; bh=qA/QDTXbRNCgEiMTWj43du7RQ3xu+VWaAzJ3TzR7K+c=; h=From:To:Subject:Date:In-Reply-To:References:From; b=o+YsCFwZ99KpJXjPqJbukLVsG5vHcaZitWUsRwSobeFFjj5cAYCIKLy4Wiqq1Gshm 3/spGQygDuRaY/hFWzn/ZjK9kKGRDFnk+qXdgJKsRh1bWQ1lW92Q7bpaigndKJhMA1 zQD6d9dFRUzPFu7bXKYIMfnNPmyIyyR3isfwNUwQ= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Tue, 7 Sep 2021 03:20:44 +0300 Message-Id: <20210907002044.7319-6-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210907002044.7319-1-laurent.pinchart@ideasonboard.com> References: <20210907002044.7319-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 5/5] qcam: viewfinder_gl: Support configurable stride in shaders X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The RGB and YUV conversion doesn't take the stride into account, neither when creating the textures, nor when sampling them. Fix it by using the stride as the texture width, and multiplying the x coordinate in the vertex shaders by a factor to only sample the active portion of the image. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham Reviewed-by: Paul Elder --- src/qcam/assets/shader/identity.vert | 4 ++- src/qcam/viewfinder_gl.cpp | 54 ++++++++++++++++++++++------ src/qcam/viewfinder_gl.h | 1 + 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/qcam/assets/shader/identity.vert b/src/qcam/assets/shader/identity.vert index 6d6f7551017e..12c41377cfe7 100644 --- a/src/qcam/assets/shader/identity.vert +++ b/src/qcam/assets/shader/identity.vert @@ -9,8 +9,10 @@ attribute vec4 vertexIn; attribute vec2 textureIn; varying vec2 textureOut; +uniform float stride_factor; + void main(void) { gl_Position = vertexIn; - textureOut = textureIn; + textureOut = vec2(textureIn.x * stride_factor, textureIn.y); } diff --git a/src/qcam/viewfinder_gl.cpp b/src/qcam/viewfinder_gl.cpp index aeb1ea02d2d5..3ae8b03accb5 100644 --- a/src/qcam/viewfinder_gl.cpp +++ b/src/qcam/viewfinder_gl.cpp @@ -395,6 +395,7 @@ bool ViewFinderGL::createFragmentShader() textureUniformV_ = shaderProgram_.uniformLocation("tex_v"); textureUniformStep_ = shaderProgram_.uniformLocation("tex_step"); textureUniformSize_ = shaderProgram_.uniformLocation("tex_size"); + textureUniformStrideFactor_ = shaderProgram_.uniformLocation("stride_factor"); textureUniformBayerFirstRed_ = shaderProgram_.uniformLocation("tex_bayer_first_red"); /* Create the textures. */ @@ -464,6 +465,9 @@ void ViewFinderGL::initializeGL() void ViewFinderGL::doRender() { + /* Stride of the first plane, in pixels. */ + unsigned int stridePixels; + switch (format_) { case libcamera::formats::NV12: case libcamera::formats::NV21: @@ -477,7 +481,7 @@ void ViewFinderGL::doRender() glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, - size_.width(), + stride_, size_.height(), 0, GL_LUMINANCE, @@ -491,13 +495,15 @@ void ViewFinderGL::doRender() glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, - size_.width() / horzSubSample_, + stride_ / horzSubSample_, size_.height() / vertSubSample_, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, image_->data(1).data()); shaderProgram_.setUniformValue(textureUniformU_, 1); + + stridePixels = stride_; break; case libcamera::formats::YUV420: @@ -507,7 +513,7 @@ void ViewFinderGL::doRender() glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, - size_.width(), + stride_, size_.height(), 0, GL_LUMINANCE, @@ -521,7 +527,7 @@ void ViewFinderGL::doRender() glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, - size_.width() / horzSubSample_, + stride_ / horzSubSample_, size_.height() / vertSubSample_, 0, GL_LUMINANCE, @@ -535,13 +541,15 @@ void ViewFinderGL::doRender() glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, - size_.width() / horzSubSample_, + stride_ / horzSubSample_, size_.height() / vertSubSample_, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image_->data(2).data()); shaderProgram_.setUniformValue(textureUniformV_, 2); + + stridePixels = stride_; break; case libcamera::formats::YVU420: @@ -551,7 +559,7 @@ void ViewFinderGL::doRender() glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, - size_.width(), + stride_, size_.height(), 0, GL_LUMINANCE, @@ -565,7 +573,7 @@ void ViewFinderGL::doRender() glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, - size_.width() / horzSubSample_, + stride_ / horzSubSample_, size_.height() / vertSubSample_, 0, GL_LUMINANCE, @@ -579,13 +587,15 @@ void ViewFinderGL::doRender() glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, - size_.width() / horzSubSample_, + stride_ / horzSubSample_, size_.height() / vertSubSample_, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image_->data(2).data()); shaderProgram_.setUniformValue(textureUniformU_, 1); + + stridePixels = stride_; break; case libcamera::formats::UYVY: @@ -602,7 +612,7 @@ void ViewFinderGL::doRender() glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, - size_.width() / 2, + stride_ / 4, size_.height(), 0, GL_RGBA, @@ -619,6 +629,8 @@ void ViewFinderGL::doRender() shaderProgram_.setUniformValue(textureUniformStep_, 1.0f / (size_.width() / 2 - 1), 1.0f /* not used */); + + stridePixels = stride_ / 2; break; case libcamera::formats::ABGR8888: @@ -630,13 +642,15 @@ void ViewFinderGL::doRender() glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, - size_.width(), + stride_ / 4, size_.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image_->data(0).data()); shaderProgram_.setUniformValue(textureUniformY_, 0); + + stridePixels = stride_ / 4; break; case libcamera::formats::BGR888: @@ -646,13 +660,15 @@ void ViewFinderGL::doRender() glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, - size_.width(), + stride_ / 3, size_.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, image_->data(0).data()); shaderProgram_.setUniformValue(textureUniformY_, 0); + + stridePixels = stride_ / 3; break; case libcamera::formats::SBGGR8: @@ -692,11 +708,27 @@ void ViewFinderGL::doRender() shaderProgram_.setUniformValue(textureUniformStep_, 1.0f / (stride_ - 1), 1.0f / (size_.height() - 1)); + + /* + * The stride is already taken into account in the shaders, set + * the generic stride factor to 1.0. + */ + stridePixels = size_.width(); break; default: + stridePixels = size_.width(); break; }; + + /* + * Compute the stride factor for the vertex shader, to map the + * horizontal texture coordinate range [0.0, 1.0] to the active portion + * of the image. + */ + shaderProgram_.setUniformValue(textureUniformStrideFactor_, + static_cast(size_.width() - 1) / + (stridePixels - 1)); } void ViewFinderGL::paintGL() diff --git a/src/qcam/viewfinder_gl.h b/src/qcam/viewfinder_gl.h index 2b2b1e86035a..37b1ddd04b1d 100644 --- a/src/qcam/viewfinder_gl.h +++ b/src/qcam/viewfinder_gl.h @@ -97,6 +97,7 @@ private: /* Raw Bayer texture parameters */ GLuint textureUniformSize_; + GLuint textureUniformStrideFactor_; GLuint textureUniformBayerFirstRed_; QPointF firstRed_;