[{"id":36881,"web_url":"https://patchwork.libcamera.org/comment/36881/","msgid":"<176346967177.880260.7387671274073865367@isaac-ThinkPad-T16-Gen-2>","date":"2025-11-18T12:41:11","subject":"Re: [PATCH v4 15/21] ipa: rkisp1: cproc: Provide a Hue control","submitter":{"id":215,"url":"https://patchwork.libcamera.org/api/people/215/","name":"Isaac Scott","email":"isaac.scott@ideasonboard.com"},"content":"Hi Kieran,\n\nThank you for the patch!\n\nReviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>\n\nQuoting Kieran Bingham (2025-11-14 00:54:19)\n> The RKISP1 supports a configurable Hue as part of the colour processing\n> unit (cproc).\n> \n> This is implemented as a phase shift of the chrominance values between\n> -90 and +87.188 degrees according to the datasheet however the type\n> itself would imply that this is a range between -90 and 89.2969.\n> \n> Implement the new control converting to the hardware scale accordingly\n> and report the applied control in the completed request metadata.\n> \n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> ---\n>  src/ipa/rkisp1/algorithms/cproc.cpp | 18 ++++++++++++++++++\n>  src/ipa/rkisp1/ipa_context.h        |  3 +++\n>  2 files changed, 21 insertions(+)\n> \n> diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp\n> index 7475e6c1b609..5389882c1685 100644\n> --- a/src/ipa/rkisp1/algorithms/cproc.cpp\n> +++ b/src/ipa/rkisp1/algorithms/cproc.cpp\n> @@ -39,6 +39,7 @@ namespace {\n>  \n>  constexpr float kDefaultBrightness = 0.0f;\n>  constexpr float kDefaultContrast = 1.0f;\n> +constexpr float kDefaultHue = 0.0f;\n>  constexpr float kDefaultSaturation = 1.0f;\n>  \n>  } /* namespace */\n> @@ -53,6 +54,8 @@ int ColorProcessing::init(IPAContext &context,\n>  \n>         cmap[&controls::Brightness] = ControlInfo(-1.0f, 0.993f, kDefaultBrightness);\n>         cmap[&controls::Contrast] = ControlInfo(0.0f, 1.993f, kDefaultContrast);\n> +       cmap[&controls::Hue] = ControlInfo(HueQ::TraitsType::min,\n> +                                          HueQ::TraitsType::max, kDefaultHue);\n>         cmap[&controls::Saturation] = ControlInfo(0.0f, 1.993f, kDefaultSaturation);\n>  \n>         return 0;\n> @@ -68,6 +71,7 @@ int ColorProcessing::configure(IPAContext &context,\n>  \n>         cproc.brightness = BrightnessQ(kDefaultBrightness);\n>         cproc.contrast = ContrastQ(kDefaultContrast);\n> +       cproc.hue = HueQ(kDefaultHue);\n>         cproc.saturation = SaturationQ(kDefaultSaturation);\n>  \n>         return 0;\n> @@ -109,6 +113,17 @@ void ColorProcessing::queueRequest(IPAContext &context,\n>                 LOG(RkISP1CProc, Debug) << \"Set contrast to \" << value.value();\n>         }\n>  \n> +       const auto &hue = controls.get(controls::Hue);\n> +       if (hue) {\n> +               HueQ value = *hue;\n> +               if (cproc.hue != value) {\n> +                       cproc.hue = value;\n> +                       update = true;\n> +               }\n> +\n> +               LOG(RkISP1CProc, Debug) << \"Set hue to \" << value.value();\n> +       }\n> +\n>         const auto saturation = controls.get(controls::Saturation);\n>         if (saturation) {\n>                 SaturationQ value = *saturation;\n> @@ -122,6 +137,7 @@ void ColorProcessing::queueRequest(IPAContext &context,\n>  \n>         frameContext.cproc.brightness = cproc.brightness;\n>         frameContext.cproc.contrast = cproc.contrast;\n> +       frameContext.cproc.hue = cproc.hue;\n>         frameContext.cproc.saturation = cproc.saturation;\n>         frameContext.cproc.update = update;\n>  }\n> @@ -142,6 +158,7 @@ void ColorProcessing::prepare([[maybe_unused]] IPAContext &context,\n>         config.setEnabled(true);\n>         config->brightness = frameContext.cproc.brightness.quantized();\n>         config->contrast = frameContext.cproc.contrast.quantized();\n> +       config->hue = frameContext.cproc.hue.quantized();\n>         config->sat = frameContext.cproc.saturation.quantized();\n>  }\n>  \n> @@ -154,6 +171,7 @@ void ColorProcessing::process([[maybe_unused]] IPAContext &context, [[maybe_unus\n>  {\n>         metadata.set(controls::Brightness, frameContext.cproc.brightness.value());\n>         metadata.set(controls::Contrast, frameContext.cproc.contrast.value());\n> +       metadata.set(controls::Hue, frameContext.cproc.hue.value());\n>         metadata.set(controls::Saturation, frameContext.cproc.saturation.value());\n>  }\n>  \n> diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h\n> index 651558bdf42d..52d458a90cc3 100644\n> --- a/src/ipa/rkisp1/ipa_context.h\n> +++ b/src/ipa/rkisp1/ipa_context.h\n> @@ -36,6 +36,7 @@ namespace ipa::rkisp1 {\n>  /* Fixed point types used by CPROC */\n>  using BrightnessQ = Q1_7;\n>  using ContrastQ = UQ1_7;\n> +using HueQ = Quantized<ScaledFixedPointQTraits<Q1_7::TraitsType, 90>>;\n>  using SaturationQ = UQ1_7;\n>  \n>  struct IPAHwSettings {\n> @@ -123,6 +124,7 @@ struct IPAActiveState {\n>         struct {\n>                 BrightnessQ brightness;\n>                 ContrastQ contrast;\n> +               HueQ hue;\n>                 SaturationQ saturation;\n>         } cproc;\n>  \n> @@ -177,6 +179,7 @@ struct IPAFrameContext : public FrameContext {\n>         struct {\n>                 BrightnessQ brightness;\n>                 ContrastQ contrast;\n> +               HueQ hue;\n>                 SaturationQ saturation;\n>  \n>                 bool update;\n> -- \n> 2.51.1\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id AECEABD80A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 18 Nov 2025 12:41:15 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5C83E60A86;\n\tTue, 18 Nov 2025 13:41:15 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A3DD5609D8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 18 Nov 2025 13:41:14 +0100 (CET)","from thinkpad.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id E34B01E33;\n\tTue, 18 Nov 2025 13:39:10 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"QKEvdTgq\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763469551;\n\tbh=81d7KAGjy+c2qyoBYWx7iyjohA7/CgdSxDUQ3eECHxw=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=QKEvdTgqWWCQv6/uXsX0I09mb1IActwBlISOFYLTQWwoCJu291AxoJYQC3jsjIQLp\n\tmVbsy84VES/7XSdEH3gINCJQf6Gfe48XgLd9zNxT1gQlMA7yETOGJV1wC+0QhjFXKH\n\t566vZJxz8zzwaPGATBHaLDgMyYMaUu98bsuTYzZw=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20251114005428.90024-16-kieran.bingham@ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-16-kieran.bingham@ideasonboard.com>","Subject":"Re: [PATCH v4 15/21] ipa: rkisp1: cproc: Provide a Hue control","From":"Isaac Scott <isaac.scott@ideasonboard.com>","Cc":"Kieran Bingham <kieran.bingham@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Date":"Tue, 18 Nov 2025 12:41:11 +0000","Message-ID":"<176346967177.880260.7387671274073865367@isaac-ThinkPad-T16-Gen-2>","User-Agent":"alot/0.10","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":36898,"web_url":"https://patchwork.libcamera.org/comment/36898/","msgid":"<20251119042926.GC17526@pendragon.ideasonboard.com>","date":"2025-11-19T04:29:26","subject":"Re: [PATCH v4 15/21] ipa: rkisp1: cproc: Provide a Hue control","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Kieran,\n\nThank you for the patch.\n\nOn Fri, Nov 14, 2025 at 12:54:19AM +0000, Kieran Bingham wrote:\n> The RKISP1 supports a configurable Hue as part of the colour processing\n> unit (cproc).\n> \n> This is implemented as a phase shift of the chrominance values between\n> -90 and +87.188 degrees according to the datasheet however the type\n> itself would imply that this is a range between -90 and 89.2969.\n> \n> Implement the new control converting to the hardware scale accordingly\n> and report the applied control in the completed request metadata.\n> \n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> ---\n>  src/ipa/rkisp1/algorithms/cproc.cpp | 18 ++++++++++++++++++\n>  src/ipa/rkisp1/ipa_context.h        |  3 +++\n>  2 files changed, 21 insertions(+)\n> \n> diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp\n> index 7475e6c1b609..5389882c1685 100644\n> --- a/src/ipa/rkisp1/algorithms/cproc.cpp\n> +++ b/src/ipa/rkisp1/algorithms/cproc.cpp\n> @@ -39,6 +39,7 @@ namespace {\n>  \n>  constexpr float kDefaultBrightness = 0.0f;\n>  constexpr float kDefaultContrast = 1.0f;\n> +constexpr float kDefaultHue = 0.0f;\n>  constexpr float kDefaultSaturation = 1.0f;\n>  \n>  } /* namespace */\n> @@ -53,6 +54,8 @@ int ColorProcessing::init(IPAContext &context,\n>  \n>  \tcmap[&controls::Brightness] = ControlInfo(-1.0f, 0.993f, kDefaultBrightness);\n>  \tcmap[&controls::Contrast] = ControlInfo(0.0f, 1.993f, kDefaultContrast);\n> +\tcmap[&controls::Hue] = ControlInfo(HueQ::TraitsType::min,\n> +\t\t\t\t\t   HueQ::TraitsType::max, kDefaultHue);\n>  \tcmap[&controls::Saturation] = ControlInfo(0.0f, 1.993f, kDefaultSaturation);\n>  \n>  \treturn 0;\n> @@ -68,6 +71,7 @@ int ColorProcessing::configure(IPAContext &context,\n>  \n>  \tcproc.brightness = BrightnessQ(kDefaultBrightness);\n>  \tcproc.contrast = ContrastQ(kDefaultContrast);\n> +\tcproc.hue = HueQ(kDefaultHue);\n>  \tcproc.saturation = SaturationQ(kDefaultSaturation);\n>  \n>  \treturn 0;\n> @@ -109,6 +113,17 @@ void ColorProcessing::queueRequest(IPAContext &context,\n>  \t\tLOG(RkISP1CProc, Debug) << \"Set contrast to \" << value.value();\n>  \t}\n>  \n> +\tconst auto &hue = controls.get(controls::Hue);\n> +\tif (hue) {\n> +\t\tHueQ value = *hue;\n> +\t\tif (cproc.hue != value) {\n> +\t\t\tcproc.hue = value;\n> +\t\t\tupdate = true;\n> +\t\t}\n> +\n> +\t\tLOG(RkISP1CProc, Debug) << \"Set hue to \" << value.value();\n> +\t}\n> +\n>  \tconst auto saturation = controls.get(controls::Saturation);\n>  \tif (saturation) {\n>  \t\tSaturationQ value = *saturation;\n> @@ -122,6 +137,7 @@ void ColorProcessing::queueRequest(IPAContext &context,\n>  \n>  \tframeContext.cproc.brightness = cproc.brightness;\n>  \tframeContext.cproc.contrast = cproc.contrast;\n> +\tframeContext.cproc.hue = cproc.hue;\n>  \tframeContext.cproc.saturation = cproc.saturation;\n>  \tframeContext.cproc.update = update;\n>  }\n> @@ -142,6 +158,7 @@ void ColorProcessing::prepare([[maybe_unused]] IPAContext &context,\n>  \tconfig.setEnabled(true);\n>  \tconfig->brightness = frameContext.cproc.brightness.quantized();\n>  \tconfig->contrast = frameContext.cproc.contrast.quantized();\n> +\tconfig->hue = frameContext.cproc.hue.quantized();\n>  \tconfig->sat = frameContext.cproc.saturation.quantized();\n>  }\n>  \n> @@ -154,6 +171,7 @@ void ColorProcessing::process([[maybe_unused]] IPAContext &context, [[maybe_unus\n>  {\n>  \tmetadata.set(controls::Brightness, frameContext.cproc.brightness.value());\n>  \tmetadata.set(controls::Contrast, frameContext.cproc.contrast.value());\n> +\tmetadata.set(controls::Hue, frameContext.cproc.hue.value());\n>  \tmetadata.set(controls::Saturation, frameContext.cproc.saturation.value());\n>  }\n>  \n> diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h\n> index 651558bdf42d..52d458a90cc3 100644\n> --- a/src/ipa/rkisp1/ipa_context.h\n> +++ b/src/ipa/rkisp1/ipa_context.h\n> @@ -36,6 +36,7 @@ namespace ipa::rkisp1 {\n>  /* Fixed point types used by CPROC */\n>  using BrightnessQ = Q1_7;\n>  using ContrastQ = UQ1_7;\n> +using HueQ = Quantized<ScaledFixedPointQTraits<Q1_7::TraitsType, 90>>;\n\nWhile I think you managed to create a quite nice Quantized type, I'm in\ntwo minds about ScaledFixedPointQTraits. It's used here only, and\ndesigning an API with a single use case in mind is always dangerous.\nShould we postpone ScaledFixedPointQTraits until we get at least a\nsecond use case, and handle the division/multiplication by 90 explicitly\nin here in the meantime ?\n\n>  using SaturationQ = UQ1_7;\n>  \n>  struct IPAHwSettings {\n> @@ -123,6 +124,7 @@ struct IPAActiveState {\n>  \tstruct {\n>  \t\tBrightnessQ brightness;\n>  \t\tContrastQ contrast;\n> +\t\tHueQ hue;\n>  \t\tSaturationQ saturation;\n>  \t} cproc;\n>  \n> @@ -177,6 +179,7 @@ struct IPAFrameContext : public FrameContext {\n>  \tstruct {\n>  \t\tBrightnessQ brightness;\n>  \t\tContrastQ contrast;\n> +\t\tHueQ hue;\n>  \t\tSaturationQ saturation;\n>  \n>  \t\tbool update;","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 44C05C0F1B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 19 Nov 2025 04:30:05 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DC5FD60A85;\n\tWed, 19 Nov 2025 05:30:04 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C0B4660A7B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 19 Nov 2025 05:30:02 +0100 (CET)","from pendragon.ideasonboard.com (unknown [205.220.129.225])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 01F5CDD9;\n\tWed, 19 Nov 2025 05:27:54 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"bCKhPBjB\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763526478;\n\tbh=n7QWKAKcm/d7Peu+TcRT9mzS81kZHHiXMt4LdTC96f0=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=bCKhPBjBG6DxqXGvrZqns/eGmsjhGszNDTG0U6H9buBDsF+5J9yLolHOTXiD50Tl5\n\tWzRNcXx3AHpXW04JK6trxtahq446Jo7DfSYc0bYRylBMAdUemX4EztFUhdmgHoxQ0x\n\t9HwmRQ1nPuBTswDZOAPDWJNxD9YlSuBG4jYF4IRI=","Date":"Wed, 19 Nov 2025 13:29:26 +0900","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Subject":"Re: [PATCH v4 15/21] ipa: rkisp1: cproc: Provide a Hue control","Message-ID":"<20251119042926.GC17526@pendragon.ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-16-kieran.bingham@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20251114005428.90024-16-kieran.bingham@ideasonboard.com>","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":37652,"web_url":"https://patchwork.libcamera.org/comment/37652/","msgid":"<176839652508.1693075.10760108831182147728@ping.linuxembedded.co.uk>","date":"2026-01-14T13:15:25","subject":"Re: [PATCH v4 15/21] ipa: rkisp1: cproc: Provide a Hue control","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Laurent Pinchart (2025-11-19 04:29:26)\n> Hi Kieran,\n> \n> Thank you for the patch.\n> \n> On Fri, Nov 14, 2025 at 12:54:19AM +0000, Kieran Bingham wrote:\n> > The RKISP1 supports a configurable Hue as part of the colour processing\n> > unit (cproc).\n> > \n> > This is implemented as a phase shift of the chrominance values between\n> > -90 and +87.188 degrees according to the datasheet however the type\n> > itself would imply that this is a range between -90 and 89.2969.\n> > \n> > Implement the new control converting to the hardware scale accordingly\n> > and report the applied control in the completed request metadata.\n> > \n> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > ---\n> >  src/ipa/rkisp1/algorithms/cproc.cpp | 18 ++++++++++++++++++\n> >  src/ipa/rkisp1/ipa_context.h        |  3 +++\n> >  2 files changed, 21 insertions(+)\n> > \n> > diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp\n> > index 7475e6c1b609..5389882c1685 100644\n> > --- a/src/ipa/rkisp1/algorithms/cproc.cpp\n> > +++ b/src/ipa/rkisp1/algorithms/cproc.cpp\n> > @@ -39,6 +39,7 @@ namespace {\n> >  \n> >  constexpr float kDefaultBrightness = 0.0f;\n> >  constexpr float kDefaultContrast = 1.0f;\n> > +constexpr float kDefaultHue = 0.0f;\n> >  constexpr float kDefaultSaturation = 1.0f;\n> >  \n> >  } /* namespace */\n> > @@ -53,6 +54,8 @@ int ColorProcessing::init(IPAContext &context,\n> >  \n> >       cmap[&controls::Brightness] = ControlInfo(-1.0f, 0.993f, kDefaultBrightness);\n> >       cmap[&controls::Contrast] = ControlInfo(0.0f, 1.993f, kDefaultContrast);\n> > +     cmap[&controls::Hue] = ControlInfo(HueQ::TraitsType::min,\n> > +                                        HueQ::TraitsType::max, kDefaultHue);\n> >       cmap[&controls::Saturation] = ControlInfo(0.0f, 1.993f, kDefaultSaturation);\n> >  \n> >       return 0;\n> > @@ -68,6 +71,7 @@ int ColorProcessing::configure(IPAContext &context,\n> >  \n> >       cproc.brightness = BrightnessQ(kDefaultBrightness);\n> >       cproc.contrast = ContrastQ(kDefaultContrast);\n> > +     cproc.hue = HueQ(kDefaultHue);\n> >       cproc.saturation = SaturationQ(kDefaultSaturation);\n> >  \n> >       return 0;\n> > @@ -109,6 +113,17 @@ void ColorProcessing::queueRequest(IPAContext &context,\n> >               LOG(RkISP1CProc, Debug) << \"Set contrast to \" << value.value();\n> >       }\n> >  \n> > +     const auto &hue = controls.get(controls::Hue);\n> > +     if (hue) {\n> > +             HueQ value = *hue;\n> > +             if (cproc.hue != value) {\n> > +                     cproc.hue = value;\n> > +                     update = true;\n> > +             }\n> > +\n> > +             LOG(RkISP1CProc, Debug) << \"Set hue to \" << value.value();\n> > +     }\n> > +\n> >       const auto saturation = controls.get(controls::Saturation);\n> >       if (saturation) {\n> >               SaturationQ value = *saturation;\n> > @@ -122,6 +137,7 @@ void ColorProcessing::queueRequest(IPAContext &context,\n> >  \n> >       frameContext.cproc.brightness = cproc.brightness;\n> >       frameContext.cproc.contrast = cproc.contrast;\n> > +     frameContext.cproc.hue = cproc.hue;\n> >       frameContext.cproc.saturation = cproc.saturation;\n> >       frameContext.cproc.update = update;\n> >  }\n> > @@ -142,6 +158,7 @@ void ColorProcessing::prepare([[maybe_unused]] IPAContext &context,\n> >       config.setEnabled(true);\n> >       config->brightness = frameContext.cproc.brightness.quantized();\n> >       config->contrast = frameContext.cproc.contrast.quantized();\n> > +     config->hue = frameContext.cproc.hue.quantized();\n> >       config->sat = frameContext.cproc.saturation.quantized();\n> >  }\n> >  \n> > @@ -154,6 +171,7 @@ void ColorProcessing::process([[maybe_unused]] IPAContext &context, [[maybe_unus\n> >  {\n> >       metadata.set(controls::Brightness, frameContext.cproc.brightness.value());\n> >       metadata.set(controls::Contrast, frameContext.cproc.contrast.value());\n> > +     metadata.set(controls::Hue, frameContext.cproc.hue.value());\n> >       metadata.set(controls::Saturation, frameContext.cproc.saturation.value());\n> >  }\n> >  \n> > diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h\n> > index 651558bdf42d..52d458a90cc3 100644\n> > --- a/src/ipa/rkisp1/ipa_context.h\n> > +++ b/src/ipa/rkisp1/ipa_context.h\n> > @@ -36,6 +36,7 @@ namespace ipa::rkisp1 {\n> >  /* Fixed point types used by CPROC */\n> >  using BrightnessQ = Q1_7;\n> >  using ContrastQ = UQ1_7;\n> > +using HueQ = Quantized<ScaledFixedPointQTraits<Q1_7::TraitsType, 90>>;\n> \n> While I think you managed to create a quite nice Quantized type, I'm in\n> two minds about ScaledFixedPointQTraits. It's used here only, and\n> designing an API with a single use case in mind is always dangerous.\n\nIt's not a (public) API here ... so I wouldn't be that worried\npersonally. It's still an internal helper...\n\n> Should we postpone ScaledFixedPointQTraits until we get at least a\n> second use case, and handle the division/multiplication by 90 explicitly\n> in here in the meantime ?\n\nIf you /want/ this changed tell me, as I'll have to rework it.\n\n--\nKieran\n\n> \n> >  using SaturationQ = UQ1_7;\n> >  \n> >  struct IPAHwSettings {\n> > @@ -123,6 +124,7 @@ struct IPAActiveState {\n> >       struct {\n> >               BrightnessQ brightness;\n> >               ContrastQ contrast;\n> > +             HueQ hue;\n> >               SaturationQ saturation;\n> >       } cproc;\n> >  \n> > @@ -177,6 +179,7 @@ struct IPAFrameContext : public FrameContext {\n> >       struct {\n> >               BrightnessQ brightness;\n> >               ContrastQ contrast;\n> > +             HueQ hue;\n> >               SaturationQ saturation;\n> >  \n> >               bool update;\n> \n> -- \n> Regards,\n> \n> Laurent Pinchart","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 18269C3226\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 14 Jan 2026 13:15:31 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 18B1A61FC3;\n\tWed, 14 Jan 2026 14:15:30 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A508061F9F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 14 Jan 2026 14:15:28 +0100 (CET)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 95F38316;\n\tWed, 14 Jan 2026 14:15:01 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"YKJijtjZ\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1768396501;\n\tbh=he66E0x2DTPMkNTcYHqq3ng3VinjWgLUcFMfnPBmrrI=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=YKJijtjZZYl54WNKR/nySMNEP2iEIEzFMC+pxOxsi9kvHSBTi/Mx6YS+zaD4TLcLt\n\tLdaSop+RmWdMNL1zLbEDBDL6RmKmhXlohYsNnr14qVB0b02j7fAfGu1UDMxZiN9dRJ\n\tQSqiKb82akLX2m0Ag2GwPgV5aG2555jT8y5IJmSI=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20251119042926.GC17526@pendragon.ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-16-kieran.bingham@ideasonboard.com>\n\t<20251119042926.GC17526@pendragon.ideasonboard.com>","Subject":"Re: [PATCH v4 15/21] ipa: rkisp1: cproc: Provide a Hue control","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Date":"Wed, 14 Jan 2026 13:15:25 +0000","Message-ID":"<176839652508.1693075.10760108831182147728@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":37654,"web_url":"https://patchwork.libcamera.org/comment/37654/","msgid":"<20260114132817.GJ30544@pendragon.ideasonboard.com>","date":"2026-01-14T13:28:17","subject":"Re: [PATCH v4 15/21] ipa: rkisp1: cproc: Provide a Hue control","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Wed, Jan 14, 2026 at 01:15:25PM +0000, Kieran Bingham wrote:\n> Quoting Laurent Pinchart (2025-11-19 04:29:26)\n> > On Fri, Nov 14, 2025 at 12:54:19AM +0000, Kieran Bingham wrote:\n> > > The RKISP1 supports a configurable Hue as part of the colour processing\n> > > unit (cproc).\n> > > \n> > > This is implemented as a phase shift of the chrominance values between\n> > > -90 and +87.188 degrees according to the datasheet however the type\n> > > itself would imply that this is a range between -90 and 89.2969.\n> > > \n> > > Implement the new control converting to the hardware scale accordingly\n> > > and report the applied control in the completed request metadata.\n> > > \n> > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > ---\n> > >  src/ipa/rkisp1/algorithms/cproc.cpp | 18 ++++++++++++++++++\n> > >  src/ipa/rkisp1/ipa_context.h        |  3 +++\n> > >  2 files changed, 21 insertions(+)\n> > > \n> > > diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp\n> > > index 7475e6c1b609..5389882c1685 100644\n> > > --- a/src/ipa/rkisp1/algorithms/cproc.cpp\n> > > +++ b/src/ipa/rkisp1/algorithms/cproc.cpp\n> > > @@ -39,6 +39,7 @@ namespace {\n> > >  \n> > >  constexpr float kDefaultBrightness = 0.0f;\n> > >  constexpr float kDefaultContrast = 1.0f;\n> > > +constexpr float kDefaultHue = 0.0f;\n> > >  constexpr float kDefaultSaturation = 1.0f;\n> > >  \n> > >  } /* namespace */\n> > > @@ -53,6 +54,8 @@ int ColorProcessing::init(IPAContext &context,\n> > >  \n> > >       cmap[&controls::Brightness] = ControlInfo(-1.0f, 0.993f, kDefaultBrightness);\n> > >       cmap[&controls::Contrast] = ControlInfo(0.0f, 1.993f, kDefaultContrast);\n> > > +     cmap[&controls::Hue] = ControlInfo(HueQ::TraitsType::min,\n> > > +                                        HueQ::TraitsType::max, kDefaultHue);\n> > >       cmap[&controls::Saturation] = ControlInfo(0.0f, 1.993f, kDefaultSaturation);\n> > >  \n> > >       return 0;\n> > > @@ -68,6 +71,7 @@ int ColorProcessing::configure(IPAContext &context,\n> > >  \n> > >       cproc.brightness = BrightnessQ(kDefaultBrightness);\n> > >       cproc.contrast = ContrastQ(kDefaultContrast);\n> > > +     cproc.hue = HueQ(kDefaultHue);\n> > >       cproc.saturation = SaturationQ(kDefaultSaturation);\n> > >  \n> > >       return 0;\n> > > @@ -109,6 +113,17 @@ void ColorProcessing::queueRequest(IPAContext &context,\n> > >               LOG(RkISP1CProc, Debug) << \"Set contrast to \" << value.value();\n> > >       }\n> > >  \n> > > +     const auto &hue = controls.get(controls::Hue);\n> > > +     if (hue) {\n> > > +             HueQ value = *hue;\n> > > +             if (cproc.hue != value) {\n> > > +                     cproc.hue = value;\n> > > +                     update = true;\n> > > +             }\n> > > +\n> > > +             LOG(RkISP1CProc, Debug) << \"Set hue to \" << value.value();\n> > > +     }\n> > > +\n> > >       const auto saturation = controls.get(controls::Saturation);\n> > >       if (saturation) {\n> > >               SaturationQ value = *saturation;\n> > > @@ -122,6 +137,7 @@ void ColorProcessing::queueRequest(IPAContext &context,\n> > >  \n> > >       frameContext.cproc.brightness = cproc.brightness;\n> > >       frameContext.cproc.contrast = cproc.contrast;\n> > > +     frameContext.cproc.hue = cproc.hue;\n> > >       frameContext.cproc.saturation = cproc.saturation;\n> > >       frameContext.cproc.update = update;\n> > >  }\n> > > @@ -142,6 +158,7 @@ void ColorProcessing::prepare([[maybe_unused]] IPAContext &context,\n> > >       config.setEnabled(true);\n> > >       config->brightness = frameContext.cproc.brightness.quantized();\n> > >       config->contrast = frameContext.cproc.contrast.quantized();\n> > > +     config->hue = frameContext.cproc.hue.quantized();\n> > >       config->sat = frameContext.cproc.saturation.quantized();\n> > >  }\n> > >  \n> > > @@ -154,6 +171,7 @@ void ColorProcessing::process([[maybe_unused]] IPAContext &context, [[maybe_unus\n> > >  {\n> > >       metadata.set(controls::Brightness, frameContext.cproc.brightness.value());\n> > >       metadata.set(controls::Contrast, frameContext.cproc.contrast.value());\n> > > +     metadata.set(controls::Hue, frameContext.cproc.hue.value());\n> > >       metadata.set(controls::Saturation, frameContext.cproc.saturation.value());\n> > >  }\n> > >  \n> > > diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h\n> > > index 651558bdf42d..52d458a90cc3 100644\n> > > --- a/src/ipa/rkisp1/ipa_context.h\n> > > +++ b/src/ipa/rkisp1/ipa_context.h\n> > > @@ -36,6 +36,7 @@ namespace ipa::rkisp1 {\n> > >  /* Fixed point types used by CPROC */\n> > >  using BrightnessQ = Q1_7;\n> > >  using ContrastQ = UQ1_7;\n> > > +using HueQ = Quantized<ScaledFixedPointQTraits<Q1_7::TraitsType, 90>>;\n> > \n> > While I think you managed to create a quite nice Quantized type, I'm in\n> > two minds about ScaledFixedPointQTraits. It's used here only, and\n> > designing an API with a single use case in mind is always dangerous.\n> \n> It's not a (public) API here ... so I wouldn't be that worried\n> personally. It's still an internal helper...\n> \n> > Should we postpone ScaledFixedPointQTraits until we get at least a\n> > second use case, and handle the division/multiplication by 90 explicitly\n> > in here in the meantime ?\n> \n> If you /want/ this changed tell me, as I'll have to rework it.\n\nIf that's not too hard, I think I would have a preference for that.\n\n> > >  using SaturationQ = UQ1_7;\n> > >  \n> > >  struct IPAHwSettings {\n> > > @@ -123,6 +124,7 @@ struct IPAActiveState {\n> > >       struct {\n> > >               BrightnessQ brightness;\n> > >               ContrastQ contrast;\n> > > +             HueQ hue;\n> > >               SaturationQ saturation;\n> > >       } cproc;\n> > >  \n> > > @@ -177,6 +179,7 @@ struct IPAFrameContext : public FrameContext {\n> > >       struct {\n> > >               BrightnessQ brightness;\n> > >               ContrastQ contrast;\n> > > +             HueQ hue;\n> > >               SaturationQ saturation;\n> > >  \n> > >               bool update;","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 399C0C3226\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 14 Jan 2026 13:28:40 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5422461FBC;\n\tWed, 14 Jan 2026 14:28:39 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5819D61F9F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 14 Jan 2026 14:28:38 +0100 (CET)","from pendragon.ideasonboard.com (81-175-209-152.bb.dnainternet.fi\n\t[81.175.209.152])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 56120316;\n\tWed, 14 Jan 2026 14:28:11 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"j3HWo3i0\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1768397291;\n\tbh=hGmgiCzYPEBMCLkA0QK5rBXvGcCNY2uDDTUoQfevnaM=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=j3HWo3i0YxvLaAREU67kTfCv+tVfvP2qfgvCEmFIDrJcuVZNyD5pxz3NLzyu1iTRh\n\tfEBMqc929HzLs6B1s5TTWm6Asbq0MoYmaZl7WJ9N8UImzI8rifotW5NzOY4qu0OIs6\n\tBEi9FeHfiTf/P+9P2uyg1ik9Ygf8cpSTBekVgQDo=","Date":"Wed, 14 Jan 2026 15:28:17 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Subject":"Re: [PATCH v4 15/21] ipa: rkisp1: cproc: Provide a Hue control","Message-ID":"<20260114132817.GJ30544@pendragon.ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-16-kieran.bingham@ideasonboard.com>\n\t<20251119042926.GC17526@pendragon.ideasonboard.com>\n\t<176839652508.1693075.10760108831182147728@ping.linuxembedded.co.uk>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<176839652508.1693075.10760108831182147728@ping.linuxembedded.co.uk>","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]