[{"id":36830,"web_url":"https://patchwork.libcamera.org/comment/36830/","msgid":"<3aeba3dd-d44f-4436-b806-3d79b2095117@ideasonboard.com>","date":"2025-11-14T18:08:03","subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"Hi\n\n2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta:\n> Frequently when handling data in IPA components we must convert and\n> store user interface values which may be floating point values, and\n> perform a specific operation or conversion to quantize this to a\n> hardware value.\n> \n> This value may be to a fixed point type, or more custom code mappings,\n> but in either case it is important to contain both the required hardware\n> value, with its effective quantized value.\n> \n> Provide a new storage type 'Quantized' which can be defined based on a\n> set of type specific Traits to perform the conversions between floats\n> and the underlying hardware type.\n> \n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> ---\n> v3:\n>   - adapt string format to [0xff:1.99] style instead of Q:0xff V:1.99\n>   - Clean up comments and copyright\n>   - Remove private initialisers - already handled by constructors\n>   - Change quantized_type to QuantizedType\n> \n>   src/ipa/libipa/meson.build   |   2 +\n>   src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n>   src/ipa/libipa/quantized.h   |  78 ++++++++++++++++++++\n>   3 files changed, 214 insertions(+)\n>   create mode 100644 src/ipa/libipa/quantized.cpp\n>   create mode 100644 src/ipa/libipa/quantized.h\n> \n> diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> index 660be94054fa..804289778f72 100644\n> --- a/src/ipa/libipa/meson.build\n> +++ b/src/ipa/libipa/meson.build\n> @@ -17,6 +17,7 @@ libipa_headers = files([\n>       'lux.h',\n>       'module.h',\n>       'pwl.h',\n> +    'quantized.h',\n>   ])\n>   \n>   libipa_sources = files([\n> @@ -36,6 +37,7 @@ libipa_sources = files([\n>       'lux.cpp',\n>       'module.cpp',\n>       'pwl.cpp',\n> +    'quantized.cpp',\n>   ])\n>   \n>   libipa_includes = include_directories('..')\n> diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp\n> new file mode 100644\n> index 000000000000..ef3ae21e9a3b\n> --- /dev/null\n> +++ b/src/ipa/libipa/quantized.cpp\n> @@ -0,0 +1,134 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2025, Ideas On Board Oy\n> + *\n> + * Helper class to manage conversions between floating point types and quantized\n> + * storage and representation of those values.\n> + */\n> +\n> +#include \"quantized.h\"\n> +\n> +/**\n> + * \\file quantized.h\n> + * \\brief Quantized storage and Quantizer representations\n> + */\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa {\n> +\n> +/**\n> + * \\struct libcamera::ipa::Quantized\n> + * \\brief Wrapper that stores a value in both quantized and floating-point form\n> + * \\tparam Traits The traits class defining the quantization behaviour\n> + *\n> + * The Quantized struct template provides a thin wrapper around a quantized\n> + * representation of a floating-point value. It uses a traits type \\a Traits\n> + * to define the conversion policy between the floating-point domain and the\n> + * quantized integer domain.\n> + *\n> + * Each Quantized instance maintains two synchronized members:\n> + *  - the quantized integer representation, and\n> + *  - the corresponding floating-point value.\n> + *\n> + * The traits type defines:\n> + *  - the integer storage type used for quantization,\n> + *  - the static conversion functions \\c fromFloat() and \\c toFloat(), and\n> + *  - optional metadata such as value ranges.\n> + *\n> + * Quantized provides convenient constructors and assignment operators from\n> + * either representation, as well as comparison and string formatting utilities.\n> + */\n> +\n> +/**\n> + * \\typedef Quantized::TraitsType\n> + * \\brief The traits policy type defining the quantization behaviour\n> + *\n> + * Exposes the associated traits type used by this Quantized instance.\n> + * This allows external code to refer to constants or metadata defined in\n> + * the traits, such as \\c TraitsType::min or \\c TraitsType::max.\n> + */\n> +\n> +/**\n> + * \\typedef Quantized::QuantizedType\n> + * \\brief The integer type used for the quantized representation\n> + *\n> + * This alias corresponds to \\c TraitsType::QuantizedType, as defined by\n> + * the traits class.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::Quantized(float x)\n> + * \\brief Construct a Quantized value from a floating-point number\n> + * \\param[in] x The floating-point value to be quantized\n> + *\n> + * Converts the floating-point input \\a x to its quantized integer\n> + * representation using the associated traits policy, and initializes\n> + * both the quantized and floating-point members.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::Quantized(QuantizedType x)\n> + * \\brief Construct a Quantized value from an existing quantized integer\n> + * \\param[in] x The quantized integer value\n> + *\n> + * Converts the quantized integer \\a x to its corresponding floating-point\n> + * value using the traits policy, and initializes both internal members.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::operator=(float x)\n> + * \\brief Assign a floating-point value to the Quantized object\n> + * \\param[in] x The floating-point value to assign\n> + * \\return A reference to the updated Quantized object\n> + *\n> + * Converts the floating-point value \\a x to its quantized integer\n> + * representation using the traits policy and updates both members.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::operator=(QuantizedType x)\n> + * \\brief Assign a quantized integer value to the Quantized object\n> + * \\param[in] x The quantized integer value to assign\n> + * \\return A reference to the updated Quantized object\n> + *\n> + * Converts the quantized integer \\a x to its corresponding floating-point\n> + * value using the traits policy and updates both members.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::value() const noexcept\n> + * \\brief Retrieve the floating-point representation\n> + * \\return The floating-point value corresponding to the quantized value\n> + */\n> +\n> +/**\n> + * \\fn Quantized::quantized() const noexcept\n> + * \\brief Retrieve the quantized integer representation\n> + * \\return The quantized integer value\n> + */\n> +\n> +/**\n> + * \\fn Quantized::toString() const\n> + * \\brief Format the quantized and floating-point values as a string\n> + * \\return A string containing the hexadecimal quantized value and its\n> + *         floating-point equivalent.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::operator==(const Quantized &other) const noexcept\n> + * \\brief Compare two Quantized objects for equality\n> + * \\param[in] other The other Quantized object to compare against\n> + * \\return True if both objects have the same quantized integer value\n> + */\n> +\n> +/**\n> + * \\fn Quantized::operator!=(const Quantized &other) const noexcept\n> + * \\brief Compare two Quantized objects for inequality\n> + * \\param[in] other The other Quantized object to compare against\n> + * \\return True if the quantized integer values differ\n> + */\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n> new file mode 100644\n> index 000000000000..db1c0d72db86\n> --- /dev/null\n> +++ b/src/ipa/libipa/quantized.h\n> @@ -0,0 +1,78 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2025, Ideas On Board Oy\n> + *\n> + * Helper class to manage conversions between floating point types and quantized\n> + * storage and representation of those values.\n> + */\n> +\n> +#pragma once\n> +\n> +#include <iomanip>\n\n<iomanip> does not seem necessary.\n\n\n> +#include <sstream>\n> +#include <stdint.h>\n\n<stdint.h> does not seem necessary.\n\n\n> +#include <type_traits>\n> +\n> +#include <libcamera/base/utils.h>\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa {\n> +\n> +template<typename Traits>\n> +struct Quantized {\n> +\tusing TraitsType = Traits;\n> +\tusing QuantizedType = typename Traits::QuantizedType;\n> +\tstatic_assert(std::is_arithmetic_v<QuantizedType>,\n> +\t\t      \"Quantized: QuantizedType must be arithmetic\");\n> +\n> +\tQuantized()\n> +\t\t: Quantized(0.0f) {}\n> +\tQuantized(float x) { *this = x; }\n> +\tQuantized(QuantizedType x) { *this = x; }\n> +\n> +\tQuantized &operator=(float x)\n> +\t{\n> +\t\tquantized_ = Traits::fromFloat(x);\n> +\t\tvalue_ = Traits::toFloat(quantized_);\n\nI think I have brought this up earlier, but I'm wondering if we might want to\nstore `x` in `value_`. For example, if a control has float type, is it expected\nthat the application should account for the quantization when comparing the\ndesired value and actual value in the request metadata to check for convergence?\n\n\n> +\t\treturn *this;\n> +\t}\n> +\n> +\tQuantized &operator=(QuantizedType x)\n> +\t{\n> +\t\tvalue_ = Traits::toFloat(x);\n> +\t\tquantized_ = x;\n> +\t\treturn *this;\n> +\t}\n> +\n> +\tfloat value() const noexcept { return value_; }\n> +\tQuantizedType quantized() const noexcept { return quantized_; }\n> +\n> +\tstd::string toString() const\n> +\t{\n> +\t\tstd::ostringstream oss;\n> +\n> +\t\toss << \"[\" << utils::hex(quantized_)\n> +\t\t    << \":\" << value_ << \"]\";\n> +\n> +\t\treturn oss.str();\n> +\t}\n\nI'd really like to have `operator<<()` and then `toString()` implemented using that.\n\n\nReviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n\n> +\n> +\tbool operator==(const Quantized &other) const noexcept\n> +\t{\n> +\t\treturn quantized_ == other.quantized_;\n> +\t}\n> +\n> +\tbool operator!=(const Quantized &other) const noexcept\n> +\t{\n> +\t\treturn !(*this == other);\n> +\t}\n> +\n> +private:\n> +\tQuantizedType quantized_;\n> +\tfloat value_;\n> +};\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */","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 CDF06C3263\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 14 Nov 2025 18:08:10 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0C30460A9E;\n\tFri, 14 Nov 2025 19:08:09 +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 35F7E606E6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 14 Nov 2025 19:08:07 +0100 (CET)","from [192.168.33.35] (185.221.143.100.nat.pool.zt.hu\n\t[185.221.143.100])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 26935397;\n\tFri, 14 Nov 2025 19:06:06 +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=\"UrnQCNux\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763143566;\n\tbh=S0rw5n33s38blZwUhvi6AmMkRivE1TsuqdLWffglERY=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=UrnQCNuxt7C44iSiWaLWQSfjO9WOkzI+cIJ0MmDEH+p6zXdtFLWC0axNW6egF2Ppo\n\tXY1K2ksDU1wQUJ55jWMaiX2wtHc+r/3XegsUkO8bhzg75fnRQFYYcLaTXCV0LcM9oQ\n\t6B66+beswdxszPHIm+aITy6XQfH2buZAhenxclBQ=","Message-ID":"<3aeba3dd-d44f-4436-b806-3d79b2095117@ideasonboard.com>","Date":"Fri, 14 Nov 2025 19:08:03 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-2-kieran.bingham@ideasonboard.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU, en-GB","In-Reply-To":"<20251114005428.90024-2-kieran.bingham@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","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":36841,"web_url":"https://patchwork.libcamera.org/comment/36841/","msgid":"<176314645440.567526.14469539936828264534@ping.linuxembedded.co.uk>","date":"2025-11-14T18:54:14","subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Barnabás Pőcze (2025-11-14 18:08:03)\n> Hi\n> \n> 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta:\n> > Frequently when handling data in IPA components we must convert and\n> > store user interface values which may be floating point values, and\n> > perform a specific operation or conversion to quantize this to a\n> > hardware value.\n> > \n> > This value may be to a fixed point type, or more custom code mappings,\n> > but in either case it is important to contain both the required hardware\n> > value, with its effective quantized value.\n> > \n> > Provide a new storage type 'Quantized' which can be defined based on a\n> > set of type specific Traits to perform the conversions between floats\n> > and the underlying hardware type.\n> > \n> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > \n> > ---\n> > v3:\n> >   - adapt string format to [0xff:1.99] style instead of Q:0xff V:1.99\n> >   - Clean up comments and copyright\n> >   - Remove private initialisers - already handled by constructors\n> >   - Change quantized_type to QuantizedType\n> > \n> >   src/ipa/libipa/meson.build   |   2 +\n> >   src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n> >   src/ipa/libipa/quantized.h   |  78 ++++++++++++++++++++\n> >   3 files changed, 214 insertions(+)\n> >   create mode 100644 src/ipa/libipa/quantized.cpp\n> >   create mode 100644 src/ipa/libipa/quantized.h\n> > \n> > diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> > index 660be94054fa..804289778f72 100644\n> > --- a/src/ipa/libipa/meson.build\n> > +++ b/src/ipa/libipa/meson.build\n> > @@ -17,6 +17,7 @@ libipa_headers = files([\n> >       'lux.h',\n> >       'module.h',\n> >       'pwl.h',\n> > +    'quantized.h',\n> >   ])\n> >   \n> >   libipa_sources = files([\n> > @@ -36,6 +37,7 @@ libipa_sources = files([\n> >       'lux.cpp',\n> >       'module.cpp',\n> >       'pwl.cpp',\n> > +    'quantized.cpp',\n> >   ])\n> >   \n> >   libipa_includes = include_directories('..')\n> > diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp\n> > new file mode 100644\n> > index 000000000000..ef3ae21e9a3b\n> > --- /dev/null\n> > +++ b/src/ipa/libipa/quantized.cpp\n> > @@ -0,0 +1,134 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2025, Ideas On Board Oy\n> > + *\n> > + * Helper class to manage conversions between floating point types and quantized\n> > + * storage and representation of those values.\n> > + */\n> > +\n> > +#include \"quantized.h\"\n> > +\n> > +/**\n> > + * \\file quantized.h\n> > + * \\brief Quantized storage and Quantizer representations\n> > + */\n> > +\n> > +namespace libcamera {\n> > +\n> > +namespace ipa {\n> > +\n> > +/**\n> > + * \\struct libcamera::ipa::Quantized\n> > + * \\brief Wrapper that stores a value in both quantized and floating-point form\n> > + * \\tparam Traits The traits class defining the quantization behaviour\n> > + *\n> > + * The Quantized struct template provides a thin wrapper around a quantized\n> > + * representation of a floating-point value. It uses a traits type \\a Traits\n> > + * to define the conversion policy between the floating-point domain and the\n> > + * quantized integer domain.\n> > + *\n> > + * Each Quantized instance maintains two synchronized members:\n> > + *  - the quantized integer representation, and\n> > + *  - the corresponding floating-point value.\n> > + *\n> > + * The traits type defines:\n> > + *  - the integer storage type used for quantization,\n> > + *  - the static conversion functions \\c fromFloat() and \\c toFloat(), and\n> > + *  - optional metadata such as value ranges.\n> > + *\n> > + * Quantized provides convenient constructors and assignment operators from\n> > + * either representation, as well as comparison and string formatting utilities.\n> > + */\n> > +\n> > +/**\n> > + * \\typedef Quantized::TraitsType\n> > + * \\brief The traits policy type defining the quantization behaviour\n> > + *\n> > + * Exposes the associated traits type used by this Quantized instance.\n> > + * This allows external code to refer to constants or metadata defined in\n> > + * the traits, such as \\c TraitsType::min or \\c TraitsType::max.\n> > + */\n> > +\n> > +/**\n> > + * \\typedef Quantized::QuantizedType\n> > + * \\brief The integer type used for the quantized representation\n> > + *\n> > + * This alias corresponds to \\c TraitsType::QuantizedType, as defined by\n> > + * the traits class.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::Quantized(float x)\n> > + * \\brief Construct a Quantized value from a floating-point number\n> > + * \\param[in] x The floating-point value to be quantized\n> > + *\n> > + * Converts the floating-point input \\a x to its quantized integer\n> > + * representation using the associated traits policy, and initializes\n> > + * both the quantized and floating-point members.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::Quantized(QuantizedType x)\n> > + * \\brief Construct a Quantized value from an existing quantized integer\n> > + * \\param[in] x The quantized integer value\n> > + *\n> > + * Converts the quantized integer \\a x to its corresponding floating-point\n> > + * value using the traits policy, and initializes both internal members.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::operator=(float x)\n> > + * \\brief Assign a floating-point value to the Quantized object\n> > + * \\param[in] x The floating-point value to assign\n> > + * \\return A reference to the updated Quantized object\n> > + *\n> > + * Converts the floating-point value \\a x to its quantized integer\n> > + * representation using the traits policy and updates both members.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::operator=(QuantizedType x)\n> > + * \\brief Assign a quantized integer value to the Quantized object\n> > + * \\param[in] x The quantized integer value to assign\n> > + * \\return A reference to the updated Quantized object\n> > + *\n> > + * Converts the quantized integer \\a x to its corresponding floating-point\n> > + * value using the traits policy and updates both members.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::value() const noexcept\n> > + * \\brief Retrieve the floating-point representation\n> > + * \\return The floating-point value corresponding to the quantized value\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::quantized() const noexcept\n> > + * \\brief Retrieve the quantized integer representation\n> > + * \\return The quantized integer value\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::toString() const\n> > + * \\brief Format the quantized and floating-point values as a string\n> > + * \\return A string containing the hexadecimal quantized value and its\n> > + *         floating-point equivalent.\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::operator==(const Quantized &other) const noexcept\n> > + * \\brief Compare two Quantized objects for equality\n> > + * \\param[in] other The other Quantized object to compare against\n> > + * \\return True if both objects have the same quantized integer value\n> > + */\n> > +\n> > +/**\n> > + * \\fn Quantized::operator!=(const Quantized &other) const noexcept\n> > + * \\brief Compare two Quantized objects for inequality\n> > + * \\param[in] other The other Quantized object to compare against\n> > + * \\return True if the quantized integer values differ\n> > + */\n> > +\n> > +} /* namespace ipa */\n> > +\n> > +} /* namespace libcamera */\n> > diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n> > new file mode 100644\n> > index 000000000000..db1c0d72db86\n> > --- /dev/null\n> > +++ b/src/ipa/libipa/quantized.h\n> > @@ -0,0 +1,78 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2025, Ideas On Board Oy\n> > + *\n> > + * Helper class to manage conversions between floating point types and quantized\n> > + * storage and representation of those values.\n> > + */\n> > +\n> > +#pragma once\n> > +\n> > +#include <iomanip>\n> \n> <iomanip> does not seem necessary.\n> \n> \n> > +#include <sstream>\n> > +#include <stdint.h>\n> \n> <stdint.h> does not seem necessary.\n> \n> \n> > +#include <type_traits>\n> > +\n> > +#include <libcamera/base/utils.h>\n> > +\n> > +namespace libcamera {\n> > +\n> > +namespace ipa {\n> > +\n> > +template<typename Traits>\n> > +struct Quantized {\n> > +     using TraitsType = Traits;\n> > +     using QuantizedType = typename Traits::QuantizedType;\n> > +     static_assert(std::is_arithmetic_v<QuantizedType>,\n> > +                   \"Quantized: QuantizedType must be arithmetic\");\n> > +\n> > +     Quantized()\n> > +             : Quantized(0.0f) {}\n> > +     Quantized(float x) { *this = x; }\n> > +     Quantized(QuantizedType x) { *this = x; }\n> > +\n> > +     Quantized &operator=(float x)\n> > +     {\n> > +             quantized_ = Traits::fromFloat(x);\n> > +             value_ = Traits::toFloat(quantized_);\n> \n> I think I have brought this up earlier, but I'm wondering if we might want to\n> store `x` in `value_`. For example, if a control has float type, is it expected\n> that the application should account for the quantization when comparing the\n> desired value and actual value in the request metadata to check for convergence?\n> \n> \n> > +             return *this;\n> > +     }\n> > +\n> > +     Quantized &operator=(QuantizedType x)\n> > +     {\n> > +             value_ = Traits::toFloat(x);\n> > +             quantized_ = x;\n> > +             return *this;\n> > +     }\n> > +\n> > +     float value() const noexcept { return value_; }\n> > +     QuantizedType quantized() const noexcept { return quantized_; }\n> > +\n> > +     std::string toString() const\n> > +     {\n> > +             std::ostringstream oss;\n> > +\n> > +             oss << \"[\" << utils::hex(quantized_)\n> > +                 << \":\" << value_ << \"]\";\n> > +\n> > +             return oss.str();\n> > +     }\n> \n> I'd really like to have `operator<<()` and then `toString()` implemented using that.\n> \n\nI'm sure in some earlier iteration I tried to add operator<<() and I\nstruggled, but now things have changed a lot I can indeed try again and\nlet you know if I struggle again.\n\n> \n> Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n> \n> > +\n> > +     bool operator==(const Quantized &other) const noexcept\n> > +     {\n> > +             return quantized_ == other.quantized_;\n> > +     }\n> > +\n> > +     bool operator!=(const Quantized &other) const noexcept\n> > +     {\n> > +             return !(*this == other);\n> > +     }\n> > +\n> > +private:\n> > +     QuantizedType quantized_;\n> > +     float value_;\n> > +};\n> > +\n> > +} /* namespace ipa */\n> > +\n> > +} /* namespace libcamera */\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 846A2C3241\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 14 Nov 2025 18:54:18 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2FE0260A81;\n\tFri, 14 Nov 2025 19:54:18 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 82D60606E6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 14 Nov 2025 19:54:17 +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 7F1A3169;\n\tFri, 14 Nov 2025 19:52:16 +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=\"a/GeVaF0\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763146336;\n\tbh=KYJezzTJfExbV07BJOlaCidEoLukIpHnWHbnX4WGiDM=;\n\th=In-Reply-To:References:Subject:From:To:Date:From;\n\tb=a/GeVaF0ePV0cdebliHeO0XWDjeAW1pHi+7m8nwyVtYUhwNt6fAlKtUoy21mk5Xub\n\tYG9Caul0Sgb+MiTE9PG7oeT2cRE6CZFhO2WY0danYPCYRbDaGFzsy21Bb2LQs3yJIK\n\t8J8hZ54slgFHALKyYfTVezNGrysGB4m3hcebYKQo=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<3aeba3dd-d44f-4436-b806-3d79b2095117@ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-2-kieran.bingham@ideasonboard.com>\n\t<3aeba3dd-d44f-4436-b806-3d79b2095117@ideasonboard.com>","Subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Date":"Fri, 14 Nov 2025 18:54:14 +0000","Message-ID":"<176314645440.567526.14469539936828264534@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":36862,"web_url":"https://patchwork.libcamera.org/comment/36862/","msgid":"<20251117124838.GA17915@pendragon.ideasonboard.com>","date":"2025-11-17T12:48:38","subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Fri, Nov 14, 2025 at 06:54:14PM +0000, Kieran Bingham wrote:\n> Quoting Barnabás Pőcze (2025-11-14 18:08:03)\n> > 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta:\n> > > Frequently when handling data in IPA components we must convert and\n> > > store user interface values which may be floating point values, and\n> > > perform a specific operation or conversion to quantize this to a\n> > > hardware value.\n> > > \n> > > This value may be to a fixed point type, or more custom code mappings,\n> > > but in either case it is important to contain both the required hardware\n> > > value, with its effective quantized value.\n> > > \n> > > Provide a new storage type 'Quantized' which can be defined based on a\n> > > set of type specific Traits to perform the conversions between floats\n> > > and the underlying hardware type.\n> > > \n> > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > \n> > > ---\n> > > v3:\n> > >   - adapt string format to [0xff:1.99] style instead of Q:0xff V:1.99\n> > >   - Clean up comments and copyright\n> > >   - Remove private initialisers - already handled by constructors\n> > >   - Change quantized_type to QuantizedType\n> > > \n> > >   src/ipa/libipa/meson.build   |   2 +\n> > >   src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n> > >   src/ipa/libipa/quantized.h   |  78 ++++++++++++++++++++\n> > >   3 files changed, 214 insertions(+)\n> > >   create mode 100644 src/ipa/libipa/quantized.cpp\n> > >   create mode 100644 src/ipa/libipa/quantized.h\n> > > \n> > > diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> > > index 660be94054fa..804289778f72 100644\n> > > --- a/src/ipa/libipa/meson.build\n> > > +++ b/src/ipa/libipa/meson.build\n> > > @@ -17,6 +17,7 @@ libipa_headers = files([\n> > >       'lux.h',\n> > >       'module.h',\n> > >       'pwl.h',\n> > > +    'quantized.h',\n> > >   ])\n> > >   \n> > >   libipa_sources = files([\n> > > @@ -36,6 +37,7 @@ libipa_sources = files([\n> > >       'lux.cpp',\n> > >       'module.cpp',\n> > >       'pwl.cpp',\n> > > +    'quantized.cpp',\n> > >   ])\n> > >   \n> > >   libipa_includes = include_directories('..')\n> > > diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp\n> > > new file mode 100644\n> > > index 000000000000..ef3ae21e9a3b\n> > > --- /dev/null\n> > > +++ b/src/ipa/libipa/quantized.cpp\n> > > @@ -0,0 +1,134 @@\n> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > +/*\n> > > + * Copyright (C) 2025, Ideas On Board Oy\n> > > + *\n> > > + * Helper class to manage conversions between floating point types and quantized\n> > > + * storage and representation of those values.\n> > > + */\n> > > +\n> > > +#include \"quantized.h\"\n> > > +\n> > > +/**\n> > > + * \\file quantized.h\n> > > + * \\brief Quantized storage and Quantizer representations\n> > > + */\n> > > +\n> > > +namespace libcamera {\n> > > +\n> > > +namespace ipa {\n> > > +\n> > > +/**\n> > > + * \\struct libcamera::ipa::Quantized\n> > > + * \\brief Wrapper that stores a value in both quantized and floating-point form\n> > > + * \\tparam Traits The traits class defining the quantization behaviour\n> > > + *\n> > > + * The Quantized struct template provides a thin wrapper around a quantized\n> > > + * representation of a floating-point value. It uses a traits type \\a Traits\n> > > + * to define the conversion policy between the floating-point domain and the\n> > > + * quantized integer domain.\n> > > + *\n> > > + * Each Quantized instance maintains two synchronized members:\n> > > + *  - the quantized integer representation, and\n> > > + *  - the corresponding floating-point value.\n> > > + *\n> > > + * The traits type defines:\n> > > + *  - the integer storage type used for quantization,\n> > > + *  - the static conversion functions \\c fromFloat() and \\c toFloat(), and\n> > > + *  - optional metadata such as value ranges.\n> > > + *\n> > > + * Quantized provides convenient constructors and assignment operators from\n> > > + * either representation, as well as comparison and string formatting utilities.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\typedef Quantized::TraitsType\n> > > + * \\brief The traits policy type defining the quantization behaviour\n> > > + *\n> > > + * Exposes the associated traits type used by this Quantized instance.\n> > > + * This allows external code to refer to constants or metadata defined in\n> > > + * the traits, such as \\c TraitsType::min or \\c TraitsType::max.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\typedef Quantized::QuantizedType\n> > > + * \\brief The integer type used for the quantized representation\n> > > + *\n> > > + * This alias corresponds to \\c TraitsType::QuantizedType, as defined by\n> > > + * the traits class.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::Quantized(float x)\n> > > + * \\brief Construct a Quantized value from a floating-point number\n> > > + * \\param[in] x The floating-point value to be quantized\n> > > + *\n> > > + * Converts the floating-point input \\a x to its quantized integer\n> > > + * representation using the associated traits policy, and initializes\n> > > + * both the quantized and floating-point members.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::Quantized(QuantizedType x)\n> > > + * \\brief Construct a Quantized value from an existing quantized integer\n> > > + * \\param[in] x The quantized integer value\n> > > + *\n> > > + * Converts the quantized integer \\a x to its corresponding floating-point\n> > > + * value using the traits policy, and initializes both internal members.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::operator=(float x)\n> > > + * \\brief Assign a floating-point value to the Quantized object\n> > > + * \\param[in] x The floating-point value to assign\n> > > + * \\return A reference to the updated Quantized object\n> > > + *\n> > > + * Converts the floating-point value \\a x to its quantized integer\n> > > + * representation using the traits policy and updates both members.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::operator=(QuantizedType x)\n> > > + * \\brief Assign a quantized integer value to the Quantized object\n> > > + * \\param[in] x The quantized integer value to assign\n> > > + * \\return A reference to the updated Quantized object\n> > > + *\n> > > + * Converts the quantized integer \\a x to its corresponding floating-point\n> > > + * value using the traits policy and updates both members.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::value() const noexcept\n> > > + * \\brief Retrieve the floating-point representation\n> > > + * \\return The floating-point value corresponding to the quantized value\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::quantized() const noexcept\n> > > + * \\brief Retrieve the quantized integer representation\n> > > + * \\return The quantized integer value\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::toString() const\n> > > + * \\brief Format the quantized and floating-point values as a string\n> > > + * \\return A string containing the hexadecimal quantized value and its\n> > > + *         floating-point equivalent.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::operator==(const Quantized &other) const noexcept\n> > > + * \\brief Compare two Quantized objects for equality\n> > > + * \\param[in] other The other Quantized object to compare against\n> > > + * \\return True if both objects have the same quantized integer value\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn Quantized::operator!=(const Quantized &other) const noexcept\n> > > + * \\brief Compare two Quantized objects for inequality\n> > > + * \\param[in] other The other Quantized object to compare against\n> > > + * \\return True if the quantized integer values differ\n> > > + */\n> > > +\n> > > +} /* namespace ipa */\n> > > +\n> > > +} /* namespace libcamera */\n> > > diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n> > > new file mode 100644\n> > > index 000000000000..db1c0d72db86\n> > > --- /dev/null\n> > > +++ b/src/ipa/libipa/quantized.h\n> > > @@ -0,0 +1,78 @@\n> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > +/*\n> > > + * Copyright (C) 2025, Ideas On Board Oy\n> > > + *\n> > > + * Helper class to manage conversions between floating point types and quantized\n> > > + * storage and representation of those values.\n> > > + */\n> > > +\n> > > +#pragma once\n> > > +\n> > > +#include <iomanip>\n> > \n> > <iomanip> does not seem necessary.\n> > \n> > > +#include <sstream>\n> > > +#include <stdint.h>\n> > \n> > <stdint.h> does not seem necessary.\n> > \n> > > +#include <type_traits>\n> > > +\n> > > +#include <libcamera/base/utils.h>\n> > > +\n> > > +namespace libcamera {\n> > > +\n> > > +namespace ipa {\n> > > +\n> > > +template<typename Traits>\n> > > +struct Quantized {\n> > > +     using TraitsType = Traits;\n> > > +     using QuantizedType = typename Traits::QuantizedType;\n> > > +     static_assert(std::is_arithmetic_v<QuantizedType>,\n> > > +                   \"Quantized: QuantizedType must be arithmetic\");\n> > > +\n> > > +     Quantized()\n> > > +             : Quantized(0.0f) {}\n> > > +     Quantized(float x) { *this = x; }\n> > > +     Quantized(QuantizedType x) { *this = x; }\n> > > +\n> > > +     Quantized &operator=(float x)\n> > > +     {\n> > > +             quantized_ = Traits::fromFloat(x);\n> > > +             value_ = Traits::toFloat(quantized_);\n> > \n> > I think I have brought this up earlier, but I'm wondering if we might want to\n> > store `x` in `value_`. For example, if a control has float type, is it expected\n> > that the application should account for the quantization when comparing the\n> > desired value and actual value in the request metadata to check for convergence?\n\nMetadata should report the exact value applied to the frame, so I think\nit should be up to the application to deal with the fact they will\nreceive rounded/quantized values.\n\nThere's however another use case for the float value. Within libcamera,\nwe often need to check if control values set in a request have changed\ncompared to the previous requests. Comparing the float value will fail.\nIs it envisioned that this would be done by first converting the float\nvalue to a Quantized instance, and then comparing the Quantized\ninstances ? If so, I think we should consider making the foat (and\npossibly QuantizedType) constructors explicit, to avoid unexpected\nbehaviour when comparing Quantized and float.\n\n> > > +             return *this;\n> > > +     }\n> > > +\n> > > +     Quantized &operator=(QuantizedType x)\n> > > +     {\n> > > +             value_ = Traits::toFloat(x);\n> > > +             quantized_ = x;\n> > > +             return *this;\n> > > +     }\n> > > +\n> > > +     float value() const noexcept { return value_; }\n> > > +     QuantizedType quantized() const noexcept { return quantized_; }\n> > > +\n> > > +     std::string toString() const\n> > > +     {\n> > > +             std::ostringstream oss;\n> > > +\n> > > +             oss << \"[\" << utils::hex(quantized_)\n> > > +                 << \":\" << value_ << \"]\";\n> > > +\n> > > +             return oss.str();\n> > > +     }\n> > \n> > I'd really like to have `operator<<()` and then `toString()` implemented using that.\n> \n> I'm sure in some earlier iteration I tried to add operator<<() and I\n> struggled, but now things have changed a lot I can indeed try again and\n> let you know if I struggle again.\n\nIf you run into problems let me know. I've had a look at operator<<()\nissues when reviewing your hex() series and researched ADL\n(https://en.cppreference.com/w/cpp/language/adl.html) issues.\n\n> > Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n> > \n> > > +\n> > > +     bool operator==(const Quantized &other) const noexcept\n> > > +     {\n> > > +             return quantized_ == other.quantized_;\n> > > +     }\n> > > +\n> > > +     bool operator!=(const Quantized &other) const noexcept\n> > > +     {\n> > > +             return !(*this == other);\n> > > +     }\n> > > +\n> > > +private:\n> > > +     QuantizedType quantized_;\n> > > +     float value_;\n> > > +};\n> > > +\n> > > +} /* namespace ipa */\n> > > +\n> > > +} /* namespace libcamera */","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 0D625C0F1B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 17 Nov 2025 12:48:58 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 31704609D8;\n\tMon, 17 Nov 2025 13:48:57 +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 254B660856\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 17 Nov 2025 13:48:55 +0100 (CET)","from pendragon.ideasonboard.com (82-203-165-222.bb.dnainternet.fi\n\t[82.203.165.222])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id DDE68FE;\n\tMon, 17 Nov 2025 13:46:51 +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=\"O0B6W4GY\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763383612;\n\tbh=grbZddIEKYdfI3PrgLY7l7+zi9E3dvEuwZjXkzk69V0=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=O0B6W4GYKAi0WhMDOCf1L8mV5s/FyVOcpuOxgS6IAcpfHA4ROOohnIay2BAQdcdNZ\n\t+oz/1/MyEGCtl/O+jVfA+3uLehZTVlV+onlTNTZdWJBxx5gWSbrpLawWd1rqUGcfkz\n\tWGJQOXOudaN6VaDbmB/Q7X+Jon6CuDHnm1N8YmKU=","Date":"Mon, 17 Nov 2025 14:48:38 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","Message-ID":"<20251117124838.GA17915@pendragon.ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-2-kieran.bingham@ideasonboard.com>\n\t<3aeba3dd-d44f-4436-b806-3d79b2095117@ideasonboard.com>\n\t<176314645440.567526.14469539936828264534@ping.linuxembedded.co.uk>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<176314645440.567526.14469539936828264534@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>"}},{"id":36867,"web_url":"https://patchwork.libcamera.org/comment/36867/","msgid":"<176346940930.880260.14935473574997159619@isaac-ThinkPad-T16-Gen-2>","date":"2025-11-18T12:36:49","subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","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:05)\n> Frequently when handling data in IPA components we must convert and\n> store user interface values which may be floating point values, and\n> perform a specific operation or conversion to quantize this to a\n> hardware value.\n> \n> This value may be to a fixed point type, or more custom code mappings,\n> but in either case it is important to contain both the required hardware\n> value, with its effective quantized value.\n> \n> Provide a new storage type 'Quantized' which can be defined based on a\n> set of type specific Traits to perform the conversions between floats\n> and the underlying hardware type.\n> \n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> ---\n> v3:\n>  - adapt string format to [0xff:1.99] style instead of Q:0xff V:1.99\n>  - Clean up comments and copyright\n>  - Remove private initialisers - already handled by constructors\n>  - Change quantized_type to QuantizedType\n> \n>  src/ipa/libipa/meson.build   |   2 +\n>  src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n>  src/ipa/libipa/quantized.h   |  78 ++++++++++++++++++++\n>  3 files changed, 214 insertions(+)\n>  create mode 100644 src/ipa/libipa/quantized.cpp\n>  create mode 100644 src/ipa/libipa/quantized.h\n> \n> diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> index 660be94054fa..804289778f72 100644\n> --- a/src/ipa/libipa/meson.build\n> +++ b/src/ipa/libipa/meson.build\n> @@ -17,6 +17,7 @@ libipa_headers = files([\n>      'lux.h',\n>      'module.h',\n>      'pwl.h',\n> +    'quantized.h',\n>  ])\n>  \n>  libipa_sources = files([\n> @@ -36,6 +37,7 @@ libipa_sources = files([\n>      'lux.cpp',\n>      'module.cpp',\n>      'pwl.cpp',\n> +    'quantized.cpp',\n>  ])\n>  \n>  libipa_includes = include_directories('..')\n> diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp\n> new file mode 100644\n> index 000000000000..ef3ae21e9a3b\n> --- /dev/null\n> +++ b/src/ipa/libipa/quantized.cpp\n> @@ -0,0 +1,134 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2025, Ideas On Board Oy\n> + *\n> + * Helper class to manage conversions between floating point types and quantized\n> + * storage and representation of those values.\n> + */\n> +\n> +#include \"quantized.h\"\n> +\n> +/**\n> + * \\file quantized.h\n> + * \\brief Quantized storage and Quantizer representations\n> + */\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa {\n> +\n> +/**\n> + * \\struct libcamera::ipa::Quantized\n> + * \\brief Wrapper that stores a value in both quantized and floating-point form\n> + * \\tparam Traits The traits class defining the quantization behaviour\n> + *\n> + * The Quantized struct template provides a thin wrapper around a quantized\n> + * representation of a floating-point value. It uses a traits type \\a Traits\n> + * to define the conversion policy between the floating-point domain and the\n> + * quantized integer domain.\n> + *\n> + * Each Quantized instance maintains two synchronized members:\n> + *  - the quantized integer representation, and\n> + *  - the corresponding floating-point value.\n> + *\n> + * The traits type defines:\n> + *  - the integer storage type used for quantization,\n> + *  - the static conversion functions \\c fromFloat() and \\c toFloat(), and\n> + *  - optional metadata such as value ranges.\n> + *\n> + * Quantized provides convenient constructors and assignment operators from\n> + * either representation, as well as comparison and string formatting utilities.\n> + */\n> +\n> +/**\n> + * \\typedef Quantized::TraitsType\n> + * \\brief The traits policy type defining the quantization behaviour\n> + *\n> + * Exposes the associated traits type used by this Quantized instance.\n> + * This allows external code to refer to constants or metadata defined in\n> + * the traits, such as \\c TraitsType::min or \\c TraitsType::max.\n> + */\n> +\n> +/**\n> + * \\typedef Quantized::QuantizedType\n> + * \\brief The integer type used for the quantized representation\n> + *\n> + * This alias corresponds to \\c TraitsType::QuantizedType, as defined by\n> + * the traits class.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::Quantized(float x)\n> + * \\brief Construct a Quantized value from a floating-point number\n> + * \\param[in] x The floating-point value to be quantized\n> + *\n> + * Converts the floating-point input \\a x to its quantized integer\n> + * representation using the associated traits policy, and initializes\n> + * both the quantized and floating-point members.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::Quantized(QuantizedType x)\n> + * \\brief Construct a Quantized value from an existing quantized integer\n> + * \\param[in] x The quantized integer value\n> + *\n> + * Converts the quantized integer \\a x to its corresponding floating-point\n> + * value using the traits policy, and initializes both internal members.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::operator=(float x)\n> + * \\brief Assign a floating-point value to the Quantized object\n> + * \\param[in] x The floating-point value to assign\n> + * \\return A reference to the updated Quantized object\n> + *\n> + * Converts the floating-point value \\a x to its quantized integer\n> + * representation using the traits policy and updates both members.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::operator=(QuantizedType x)\n> + * \\brief Assign a quantized integer value to the Quantized object\n> + * \\param[in] x The quantized integer value to assign\n> + * \\return A reference to the updated Quantized object\n> + *\n> + * Converts the quantized integer \\a x to its corresponding floating-point\n> + * value using the traits policy and updates both members.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::value() const noexcept\n> + * \\brief Retrieve the floating-point representation\n> + * \\return The floating-point value corresponding to the quantized value\n> + */\n> +\n> +/**\n> + * \\fn Quantized::quantized() const noexcept\n> + * \\brief Retrieve the quantized integer representation\n> + * \\return The quantized integer value\n> + */\n> +\n> +/**\n> + * \\fn Quantized::toString() const\n> + * \\brief Format the quantized and floating-point values as a string\n> + * \\return A string containing the hexadecimal quantized value and its\n> + *         floating-point equivalent.\n> + */\n> +\n> +/**\n> + * \\fn Quantized::operator==(const Quantized &other) const noexcept\n> + * \\brief Compare two Quantized objects for equality\n> + * \\param[in] other The other Quantized object to compare against\n> + * \\return True if both objects have the same quantized integer value\n> + */\n> +\n> +/**\n> + * \\fn Quantized::operator!=(const Quantized &other) const noexcept\n> + * \\brief Compare two Quantized objects for inequality\n> + * \\param[in] other The other Quantized object to compare against\n> + * \\return True if the quantized integer values differ\n> + */\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n> new file mode 100644\n> index 000000000000..db1c0d72db86\n> --- /dev/null\n> +++ b/src/ipa/libipa/quantized.h\n> @@ -0,0 +1,78 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2025, Ideas On Board Oy\n> + *\n> + * Helper class to manage conversions between floating point types and quantized\n> + * storage and representation of those values.\n> + */\n> +\n> +#pragma once\n> +\n> +#include <iomanip>\n> +#include <sstream>\n> +#include <stdint.h>\n> +#include <type_traits>\n> +\n> +#include <libcamera/base/utils.h>\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa {\n> +\n> +template<typename Traits>\n> +struct Quantized {\n> +       using TraitsType = Traits;\n> +       using QuantizedType = typename Traits::QuantizedType;\n> +       static_assert(std::is_arithmetic_v<QuantizedType>,\n> +                     \"Quantized: QuantizedType must be arithmetic\");\n> +\n> +       Quantized()\n> +               : Quantized(0.0f) {}\n> +       Quantized(float x) { *this = x; }\n> +       Quantized(QuantizedType x) { *this = x; }\n> +\n> +       Quantized &operator=(float x)\n> +       {\n> +               quantized_ = Traits::fromFloat(x);\n> +               value_ = Traits::toFloat(quantized_);\n> +               return *this;\n> +       }\n> +\n> +       Quantized &operator=(QuantizedType x)\n> +       {\n> +               value_ = Traits::toFloat(x);\n> +               quantized_ = x;\n> +               return *this;\n> +       }\n> +\n> +       float value() const noexcept { return value_; }\n> +       QuantizedType quantized() const noexcept { return quantized_; }\n> +\n> +       std::string toString() const\n> +       {\n> +               std::ostringstream oss;\n> +\n> +               oss << \"[\" << utils::hex(quantized_)\n> +                   << \":\" << value_ << \"]\";\n> +\n> +               return oss.str();\n> +       }\n> +\n> +       bool operator==(const Quantized &other) const noexcept\n> +       {\n> +               return quantized_ == other.quantized_;\n> +       }\n> +\n> +       bool operator!=(const Quantized &other) const noexcept\n> +       {\n> +               return !(*this == other);\n> +       }\n> +\n> +private:\n> +       QuantizedType quantized_;\n> +       float value_;\n> +};\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\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 7037ABD80A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 18 Nov 2025 12:36:55 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2B69960A8B;\n\tTue, 18 Nov 2025 13:36:54 +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 9B6E6606D5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 18 Nov 2025 13:36:52 +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 76D75D52;\n\tTue, 18 Nov 2025 13:34:48 +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=\"ltzvYny2\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763469288;\n\tbh=O9vn5k7FvoQUMUjosGnuynZ4Eeo7BezJIwaJwpsTObo=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=ltzvYny2i7ijzYd3bEbLfJ3aY6qsA8Kv8IP/R+zcxll9La9lszzwKbi+jLHUPAxas\n\t5/eB+/xXsoQHGXGo+q0rYAWWjncJnzdAjT6I9UolDd10WJ0g4TfqACdIUTOaeqPzHc\n\t79K4DF6EqOANkvGGbqAUpcmxRtrB4OEfcicMbZFg=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20251114005428.90024-2-kieran.bingham@ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-2-kieran.bingham@ideasonboard.com>","Subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","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:36:49 +0000","Message-ID":"<176346940930.880260.14935473574997159619@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":36889,"web_url":"https://patchwork.libcamera.org/comment/36889/","msgid":"<176350708304.3013501.15656843421587014881@ping.linuxembedded.co.uk>","date":"2025-11-18T23:04:43","subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","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-17 12:48:38)\n> On Fri, Nov 14, 2025 at 06:54:14PM +0000, Kieran Bingham wrote:\n> > Quoting Barnabás Pőcze (2025-11-14 18:08:03)\n> > > 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta:\n> > > > Frequently when handling data in IPA components we must convert and\n> > > > store user interface values which may be floating point values, and\n> > > > perform a specific operation or conversion to quantize this to a\n> > > > hardware value.\n> > > > \n> > > > This value may be to a fixed point type, or more custom code mappings,\n> > > > but in either case it is important to contain both the required hardware\n> > > > value, with its effective quantized value.\n> > > > \n> > > > Provide a new storage type 'Quantized' which can be defined based on a\n> > > > set of type specific Traits to perform the conversions between floats\n> > > > and the underlying hardware type.\n> > > > \n> > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > > \n> > > > ---\n> > > > v3:\n> > > >   - adapt string format to [0xff:1.99] style instead of Q:0xff V:1.99\n> > > >   - Clean up comments and copyright\n> > > >   - Remove private initialisers - already handled by constructors\n> > > >   - Change quantized_type to QuantizedType\n> > > > \n> > > >   src/ipa/libipa/meson.build   |   2 +\n> > > >   src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n> > > >   src/ipa/libipa/quantized.h   |  78 ++++++++++++++++++++\n> > > >   3 files changed, 214 insertions(+)\n> > > >   create mode 100644 src/ipa/libipa/quantized.cpp\n> > > >   create mode 100644 src/ipa/libipa/quantized.h\n> > > > \n> > > > diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> > > > index 660be94054fa..804289778f72 100644\n> > > > --- a/src/ipa/libipa/meson.build\n> > > > +++ b/src/ipa/libipa/meson.build\n> > > > @@ -17,6 +17,7 @@ libipa_headers = files([\n> > > >       'lux.h',\n> > > >       'module.h',\n> > > >       'pwl.h',\n> > > > +    'quantized.h',\n> > > >   ])\n> > > >   \n> > > >   libipa_sources = files([\n> > > > @@ -36,6 +37,7 @@ libipa_sources = files([\n> > > >       'lux.cpp',\n> > > >       'module.cpp',\n> > > >       'pwl.cpp',\n> > > > +    'quantized.cpp',\n> > > >   ])\n> > > >   \n> > > >   libipa_includes = include_directories('..')\n> > > > diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp\n> > > > new file mode 100644\n> > > > index 000000000000..ef3ae21e9a3b\n> > > > --- /dev/null\n> > > > +++ b/src/ipa/libipa/quantized.cpp\n> > > > @@ -0,0 +1,134 @@\n> > > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > > +/*\n> > > > + * Copyright (C) 2025, Ideas On Board Oy\n> > > > + *\n> > > > + * Helper class to manage conversions between floating point types and quantized\n> > > > + * storage and representation of those values.\n> > > > + */\n> > > > +\n> > > > +#include \"quantized.h\"\n> > > > +\n> > > > +/**\n> > > > + * \\file quantized.h\n> > > > + * \\brief Quantized storage and Quantizer representations\n> > > > + */\n> > > > +\n> > > > +namespace libcamera {\n> > > > +\n> > > > +namespace ipa {\n> > > > +\n> > > > +/**\n> > > > + * \\struct libcamera::ipa::Quantized\n> > > > + * \\brief Wrapper that stores a value in both quantized and floating-point form\n> > > > + * \\tparam Traits The traits class defining the quantization behaviour\n> > > > + *\n> > > > + * The Quantized struct template provides a thin wrapper around a quantized\n> > > > + * representation of a floating-point value. It uses a traits type \\a Traits\n> > > > + * to define the conversion policy between the floating-point domain and the\n> > > > + * quantized integer domain.\n> > > > + *\n> > > > + * Each Quantized instance maintains two synchronized members:\n> > > > + *  - the quantized integer representation, and\n> > > > + *  - the corresponding floating-point value.\n> > > > + *\n> > > > + * The traits type defines:\n> > > > + *  - the integer storage type used for quantization,\n> > > > + *  - the static conversion functions \\c fromFloat() and \\c toFloat(), and\n> > > > + *  - optional metadata such as value ranges.\n> > > > + *\n> > > > + * Quantized provides convenient constructors and assignment operators from\n> > > > + * either representation, as well as comparison and string formatting utilities.\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\typedef Quantized::TraitsType\n> > > > + * \\brief The traits policy type defining the quantization behaviour\n> > > > + *\n> > > > + * Exposes the associated traits type used by this Quantized instance.\n> > > > + * This allows external code to refer to constants or metadata defined in\n> > > > + * the traits, such as \\c TraitsType::min or \\c TraitsType::max.\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\typedef Quantized::QuantizedType\n> > > > + * \\brief The integer type used for the quantized representation\n> > > > + *\n> > > > + * This alias corresponds to \\c TraitsType::QuantizedType, as defined by\n> > > > + * the traits class.\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\fn Quantized::Quantized(float x)\n> > > > + * \\brief Construct a Quantized value from a floating-point number\n> > > > + * \\param[in] x The floating-point value to be quantized\n> > > > + *\n> > > > + * Converts the floating-point input \\a x to its quantized integer\n> > > > + * representation using the associated traits policy, and initializes\n> > > > + * both the quantized and floating-point members.\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\fn Quantized::Quantized(QuantizedType x)\n> > > > + * \\brief Construct a Quantized value from an existing quantized integer\n> > > > + * \\param[in] x The quantized integer value\n> > > > + *\n> > > > + * Converts the quantized integer \\a x to its corresponding floating-point\n> > > > + * value using the traits policy, and initializes both internal members.\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\fn Quantized::operator=(float x)\n> > > > + * \\brief Assign a floating-point value to the Quantized object\n> > > > + * \\param[in] x The floating-point value to assign\n> > > > + * \\return A reference to the updated Quantized object\n> > > > + *\n> > > > + * Converts the floating-point value \\a x to its quantized integer\n> > > > + * representation using the traits policy and updates both members.\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\fn Quantized::operator=(QuantizedType x)\n> > > > + * \\brief Assign a quantized integer value to the Quantized object\n> > > > + * \\param[in] x The quantized integer value to assign\n> > > > + * \\return A reference to the updated Quantized object\n> > > > + *\n> > > > + * Converts the quantized integer \\a x to its corresponding floating-point\n> > > > + * value using the traits policy and updates both members.\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\fn Quantized::value() const noexcept\n> > > > + * \\brief Retrieve the floating-point representation\n> > > > + * \\return The floating-point value corresponding to the quantized value\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\fn Quantized::quantized() const noexcept\n> > > > + * \\brief Retrieve the quantized integer representation\n> > > > + * \\return The quantized integer value\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\fn Quantized::toString() const\n> > > > + * \\brief Format the quantized and floating-point values as a string\n> > > > + * \\return A string containing the hexadecimal quantized value and its\n> > > > + *         floating-point equivalent.\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\fn Quantized::operator==(const Quantized &other) const noexcept\n> > > > + * \\brief Compare two Quantized objects for equality\n> > > > + * \\param[in] other The other Quantized object to compare against\n> > > > + * \\return True if both objects have the same quantized integer value\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\fn Quantized::operator!=(const Quantized &other) const noexcept\n> > > > + * \\brief Compare two Quantized objects for inequality\n> > > > + * \\param[in] other The other Quantized object to compare against\n> > > > + * \\return True if the quantized integer values differ\n> > > > + */\n> > > > +\n> > > > +} /* namespace ipa */\n> > > > +\n> > > > +} /* namespace libcamera */\n> > > > diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n> > > > new file mode 100644\n> > > > index 000000000000..db1c0d72db86\n> > > > --- /dev/null\n> > > > +++ b/src/ipa/libipa/quantized.h\n> > > > @@ -0,0 +1,78 @@\n> > > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > > +/*\n> > > > + * Copyright (C) 2025, Ideas On Board Oy\n> > > > + *\n> > > > + * Helper class to manage conversions between floating point types and quantized\n> > > > + * storage and representation of those values.\n> > > > + */\n> > > > +\n> > > > +#pragma once\n> > > > +\n> > > > +#include <iomanip>\n> > > \n> > > <iomanip> does not seem necessary.\n> > > \n> > > > +#include <sstream>\n> > > > +#include <stdint.h>\n> > > \n> > > <stdint.h> does not seem necessary.\n> > > \n> > > > +#include <type_traits>\n> > > > +\n> > > > +#include <libcamera/base/utils.h>\n> > > > +\n> > > > +namespace libcamera {\n> > > > +\n> > > > +namespace ipa {\n> > > > +\n> > > > +template<typename Traits>\n> > > > +struct Quantized {\n> > > > +     using TraitsType = Traits;\n> > > > +     using QuantizedType = typename Traits::QuantizedType;\n> > > > +     static_assert(std::is_arithmetic_v<QuantizedType>,\n> > > > +                   \"Quantized: QuantizedType must be arithmetic\");\n> > > > +\n> > > > +     Quantized()\n> > > > +             : Quantized(0.0f) {}\n> > > > +     Quantized(float x) { *this = x; }\n> > > > +     Quantized(QuantizedType x) { *this = x; }\n> > > > +\n> > > > +     Quantized &operator=(float x)\n> > > > +     {\n> > > > +             quantized_ = Traits::fromFloat(x);\n> > > > +             value_ = Traits::toFloat(quantized_);\n> > > \n> > > I think I have brought this up earlier, but I'm wondering if we might want to\n> > > store `x` in `value_`. For example, if a control has float type, is it expected\n> > > that the application should account for the quantization when comparing the\n> > > desired value and actual value in the request metadata to check for convergence?\n> \n> Metadata should report the exact value applied to the frame, so I think\n> it should be up to the application to deal with the fact they will\n> receive rounded/quantized values.\n> \n> There's however another use case for the float value. Within libcamera,\n> we often need to check if control values set in a request have changed\n> compared to the previous requests. Comparing the float value will fail.\n> Is it envisioned that this would be done by first converting the float\n> value to a Quantized instance, and then comparing the Quantized\n> instances ? If so, I think we should consider making the foat (and\n> possibly QuantizedType) constructors explicit, to avoid unexpected\n> behaviour when comparing Quantized and float.\n\nI don't yet understand why you need explicit constructors for this, or\nwhat specifically your asking to (remove?)\n\nLater in the series shows where this exact use case is added:\n\n\t/* Only process if the control is in the request */\n\tconst auto &hue = controls.get(controls::Hue);\n\tif (hue) {\n\t\t/* Immediately quantize the incoming value */\n\t\tHueQ value = *hue;\n\t\t/* This compares *quantized* values, and cproc.hue is a\n\t\t * quantized type so this compares the quantized not the\n\t\t * float correctly.*/\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\nIs there something specific you're concerned about or would like to\ninhibit ?\n\n\n--\nKieran\n\n\n\n\n> \n> > > > +             return *this;\n> > > > +     }\n> > > > +\n> > > > +     Quantized &operator=(QuantizedType x)\n> > > > +     {\n> > > > +             value_ = Traits::toFloat(x);\n> > > > +             quantized_ = x;\n> > > > +             return *this;\n> > > > +     }\n> > > > +\n> > > > +     float value() const noexcept { return value_; }\n> > > > +     QuantizedType quantized() const noexcept { return quantized_; }\n> > > > +\n> > > > +     std::string toString() const\n> > > > +     {\n> > > > +             std::ostringstream oss;\n> > > > +\n> > > > +             oss << \"[\" << utils::hex(quantized_)\n> > > > +                 << \":\" << value_ << \"]\";\n> > > > +\n> > > > +             return oss.str();\n> > > > +     }\n> > > \n> > > I'd really like to have `operator<<()` and then `toString()` implemented using that.\n> > \n> > I'm sure in some earlier iteration I tried to add operator<<() and I\n> > struggled, but now things have changed a lot I can indeed try again and\n> > let you know if I struggle again.\n> \n> If you run into problems let me know. I've had a look at operator<<()\n> issues when reviewing your hex() series and researched ADL\n> (https://en.cppreference.com/w/cpp/language/adl.html) issues.\n> \n> > > Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n> > > \n> > > > +\n> > > > +     bool operator==(const Quantized &other) const noexcept\n> > > > +     {\n> > > > +             return quantized_ == other.quantized_;\n> > > > +     }\n> > > > +\n> > > > +     bool operator!=(const Quantized &other) const noexcept\n> > > > +     {\n> > > > +             return !(*this == other);\n> > > > +     }\n> > > > +\n> > > > +private:\n> > > > +     QuantizedType quantized_;\n> > > > +     float value_;\n> > > > +};\n> > > > +\n> > > > +} /* namespace ipa */\n> > > > +\n> > > > +} /* namespace libcamera */\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 F0B55BD80A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 18 Nov 2025 23:04:48 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C7E0B60A8B;\n\tWed, 19 Nov 2025 00:04:47 +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 C258F60856\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 19 Nov 2025 00:04:45 +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 9A4EB965;\n\tWed, 19 Nov 2025 00:02:41 +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=\"Esm39slH\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763506961;\n\tbh=8rokoESUiotG0UIVY4auBK77xONauy4lZZOVy1ooIUI=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=Esm39slHHX6TqxzNlXSNXjcdhYb+Ibbo36mgK0SzoIjnOBrXLOGSNz80nqOZ8/m8H\n\t+q24g2S99SqNTRGRgA3mQTtUTBUcK1g/TZe8NiN1+OSW276XSla3MqMeLbhZv/h9Y5\n\t+nr5mFwQ0GOwxinRiRJTLewZGmVARSm0UrJFgOhQ=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20251117124838.GA17915@pendragon.ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-2-kieran.bingham@ideasonboard.com>\n\t<3aeba3dd-d44f-4436-b806-3d79b2095117@ideasonboard.com>\n\t<176314645440.567526.14469539936828264534@ping.linuxembedded.co.uk>\n\t<20251117124838.GA17915@pendragon.ideasonboard.com>","Subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Date":"Tue, 18 Nov 2025 23:04:43 +0000","Message-ID":"<176350708304.3013501.15656843421587014881@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":36911,"web_url":"https://patchwork.libcamera.org/comment/36911/","msgid":"<20251119051236.GZ10711@pendragon.ideasonboard.com>","date":"2025-11-19T05:12:36","subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Tue, Nov 18, 2025 at 11:04:43PM +0000, Kieran Bingham wrote:\n> Quoting Laurent Pinchart (2025-11-17 12:48:38)\n> > On Fri, Nov 14, 2025 at 06:54:14PM +0000, Kieran Bingham wrote:\n> > > Quoting Barnabás Pőcze (2025-11-14 18:08:03)\n> > > > 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta:\n> > > > > Frequently when handling data in IPA components we must convert and\n> > > > > store user interface values which may be floating point values, and\n> > > > > perform a specific operation or conversion to quantize this to a\n> > > > > hardware value.\n> > > > > \n> > > > > This value may be to a fixed point type, or more custom code mappings,\n> > > > > but in either case it is important to contain both the required hardware\n> > > > > value, with its effective quantized value.\n> > > > > \n> > > > > Provide a new storage type 'Quantized' which can be defined based on a\n> > > > > set of type specific Traits to perform the conversions between floats\n> > > > > and the underlying hardware type.\n> > > > > \n> > > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > > > \n> > > > > ---\n> > > > > v3:\n> > > > >   - adapt string format to [0xff:1.99] style instead of Q:0xff V:1.99\n> > > > >   - Clean up comments and copyright\n> > > > >   - Remove private initialisers - already handled by constructors\n> > > > >   - Change quantized_type to QuantizedType\n> > > > > \n> > > > >   src/ipa/libipa/meson.build   |   2 +\n> > > > >   src/ipa/libipa/quantized.cpp | 134 +++++++++++++++++++++++++++++++++++\n> > > > >   src/ipa/libipa/quantized.h   |  78 ++++++++++++++++++++\n> > > > >   3 files changed, 214 insertions(+)\n> > > > >   create mode 100644 src/ipa/libipa/quantized.cpp\n> > > > >   create mode 100644 src/ipa/libipa/quantized.h\n> > > > > \n> > > > > diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> > > > > index 660be94054fa..804289778f72 100644\n> > > > > --- a/src/ipa/libipa/meson.build\n> > > > > +++ b/src/ipa/libipa/meson.build\n> > > > > @@ -17,6 +17,7 @@ libipa_headers = files([\n> > > > >       'lux.h',\n> > > > >       'module.h',\n> > > > >       'pwl.h',\n> > > > > +    'quantized.h',\n> > > > >   ])\n> > > > >   \n> > > > >   libipa_sources = files([\n> > > > > @@ -36,6 +37,7 @@ libipa_sources = files([\n> > > > >       'lux.cpp',\n> > > > >       'module.cpp',\n> > > > >       'pwl.cpp',\n> > > > > +    'quantized.cpp',\n> > > > >   ])\n> > > > >   \n> > > > >   libipa_includes = include_directories('..')\n> > > > > diff --git a/src/ipa/libipa/quantized.cpp b/src/ipa/libipa/quantized.cpp\n> > > > > new file mode 100644\n> > > > > index 000000000000..ef3ae21e9a3b\n> > > > > --- /dev/null\n> > > > > +++ b/src/ipa/libipa/quantized.cpp\n> > > > > @@ -0,0 +1,134 @@\n> > > > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > > > +/*\n> > > > > + * Copyright (C) 2025, Ideas On Board Oy\n> > > > > + *\n> > > > > + * Helper class to manage conversions between floating point types and quantized\n> > > > > + * storage and representation of those values.\n> > > > > + */\n> > > > > +\n> > > > > +#include \"quantized.h\"\n> > > > > +\n> > > > > +/**\n> > > > > + * \\file quantized.h\n> > > > > + * \\brief Quantized storage and Quantizer representations\n> > > > > + */\n> > > > > +\n> > > > > +namespace libcamera {\n> > > > > +\n> > > > > +namespace ipa {\n> > > > > +\n> > > > > +/**\n> > > > > + * \\struct libcamera::ipa::Quantized\n> > > > > + * \\brief Wrapper that stores a value in both quantized and floating-point form\n> > > > > + * \\tparam Traits The traits class defining the quantization behaviour\n> > > > > + *\n> > > > > + * The Quantized struct template provides a thin wrapper around a quantized\n> > > > > + * representation of a floating-point value. It uses a traits type \\a Traits\n> > > > > + * to define the conversion policy between the floating-point domain and the\n> > > > > + * quantized integer domain.\n> > > > > + *\n> > > > > + * Each Quantized instance maintains two synchronized members:\n> > > > > + *  - the quantized integer representation, and\n> > > > > + *  - the corresponding floating-point value.\n> > > > > + *\n> > > > > + * The traits type defines:\n> > > > > + *  - the integer storage type used for quantization,\n> > > > > + *  - the static conversion functions \\c fromFloat() and \\c toFloat(), and\n> > > > > + *  - optional metadata such as value ranges.\n> > > > > + *\n> > > > > + * Quantized provides convenient constructors and assignment operators from\n> > > > > + * either representation, as well as comparison and string formatting utilities.\n> > > > > + */\n> > > > > +\n> > > > > +/**\n> > > > > + * \\typedef Quantized::TraitsType\n> > > > > + * \\brief The traits policy type defining the quantization behaviour\n> > > > > + *\n> > > > > + * Exposes the associated traits type used by this Quantized instance.\n> > > > > + * This allows external code to refer to constants or metadata defined in\n> > > > > + * the traits, such as \\c TraitsType::min or \\c TraitsType::max.\n> > > > > + */\n> > > > > +\n> > > > > +/**\n> > > > > + * \\typedef Quantized::QuantizedType\n> > > > > + * \\brief The integer type used for the quantized representation\n> > > > > + *\n> > > > > + * This alias corresponds to \\c TraitsType::QuantizedType, as defined by\n> > > > > + * the traits class.\n> > > > > + */\n> > > > > +\n> > > > > +/**\n> > > > > + * \\fn Quantized::Quantized(float x)\n> > > > > + * \\brief Construct a Quantized value from a floating-point number\n> > > > > + * \\param[in] x The floating-point value to be quantized\n> > > > > + *\n> > > > > + * Converts the floating-point input \\a x to its quantized integer\n> > > > > + * representation using the associated traits policy, and initializes\n> > > > > + * both the quantized and floating-point members.\n> > > > > + */\n> > > > > +\n> > > > > +/**\n> > > > > + * \\fn Quantized::Quantized(QuantizedType x)\n> > > > > + * \\brief Construct a Quantized value from an existing quantized integer\n> > > > > + * \\param[in] x The quantized integer value\n> > > > > + *\n> > > > > + * Converts the quantized integer \\a x to its corresponding floating-point\n> > > > > + * value using the traits policy, and initializes both internal members.\n> > > > > + */\n> > > > > +\n> > > > > +/**\n> > > > > + * \\fn Quantized::operator=(float x)\n> > > > > + * \\brief Assign a floating-point value to the Quantized object\n> > > > > + * \\param[in] x The floating-point value to assign\n> > > > > + * \\return A reference to the updated Quantized object\n> > > > > + *\n> > > > > + * Converts the floating-point value \\a x to its quantized integer\n> > > > > + * representation using the traits policy and updates both members.\n> > > > > + */\n> > > > > +\n> > > > > +/**\n> > > > > + * \\fn Quantized::operator=(QuantizedType x)\n> > > > > + * \\brief Assign a quantized integer value to the Quantized object\n> > > > > + * \\param[in] x The quantized integer value to assign\n> > > > > + * \\return A reference to the updated Quantized object\n> > > > > + *\n> > > > > + * Converts the quantized integer \\a x to its corresponding floating-point\n> > > > > + * value using the traits policy and updates both members.\n> > > > > + */\n> > > > > +\n> > > > > +/**\n> > > > > + * \\fn Quantized::value() const noexcept\n> > > > > + * \\brief Retrieve the floating-point representation\n> > > > > + * \\return The floating-point value corresponding to the quantized value\n> > > > > + */\n> > > > > +\n> > > > > +/**\n> > > > > + * \\fn Quantized::quantized() const noexcept\n> > > > > + * \\brief Retrieve the quantized integer representation\n> > > > > + * \\return The quantized integer value\n> > > > > + */\n> > > > > +\n> > > > > +/**\n> > > > > + * \\fn Quantized::toString() const\n> > > > > + * \\brief Format the quantized and floating-point values as a string\n> > > > > + * \\return A string containing the hexadecimal quantized value and its\n> > > > > + *         floating-point equivalent.\n> > > > > + */\n> > > > > +\n> > > > > +/**\n> > > > > + * \\fn Quantized::operator==(const Quantized &other) const noexcept\n> > > > > + * \\brief Compare two Quantized objects for equality\n> > > > > + * \\param[in] other The other Quantized object to compare against\n> > > > > + * \\return True if both objects have the same quantized integer value\n> > > > > + */\n> > > > > +\n> > > > > +/**\n> > > > > + * \\fn Quantized::operator!=(const Quantized &other) const noexcept\n> > > > > + * \\brief Compare two Quantized objects for inequality\n> > > > > + * \\param[in] other The other Quantized object to compare against\n> > > > > + * \\return True if the quantized integer values differ\n> > > > > + */\n> > > > > +\n> > > > > +} /* namespace ipa */\n> > > > > +\n> > > > > +} /* namespace libcamera */\n> > > > > diff --git a/src/ipa/libipa/quantized.h b/src/ipa/libipa/quantized.h\n> > > > > new file mode 100644\n> > > > > index 000000000000..db1c0d72db86\n> > > > > --- /dev/null\n> > > > > +++ b/src/ipa/libipa/quantized.h\n> > > > > @@ -0,0 +1,78 @@\n> > > > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > > > +/*\n> > > > > + * Copyright (C) 2025, Ideas On Board Oy\n> > > > > + *\n> > > > > + * Helper class to manage conversions between floating point types and quantized\n> > > > > + * storage and representation of those values.\n> > > > > + */\n> > > > > +\n> > > > > +#pragma once\n> > > > > +\n> > > > > +#include <iomanip>\n> > > > \n> > > > <iomanip> does not seem necessary.\n> > > > \n> > > > > +#include <sstream>\n> > > > > +#include <stdint.h>\n> > > > \n> > > > <stdint.h> does not seem necessary.\n> > > > \n> > > > > +#include <type_traits>\n> > > > > +\n> > > > > +#include <libcamera/base/utils.h>\n> > > > > +\n> > > > > +namespace libcamera {\n> > > > > +\n> > > > > +namespace ipa {\n> > > > > +\n> > > > > +template<typename Traits>\n> > > > > +struct Quantized {\n> > > > > +     using TraitsType = Traits;\n> > > > > +     using QuantizedType = typename Traits::QuantizedType;\n> > > > > +     static_assert(std::is_arithmetic_v<QuantizedType>,\n> > > > > +                   \"Quantized: QuantizedType must be arithmetic\");\n> > > > > +\n> > > > > +     Quantized()\n> > > > > +             : Quantized(0.0f) {}\n> > > > > +     Quantized(float x) { *this = x; }\n> > > > > +     Quantized(QuantizedType x) { *this = x; }\n> > > > > +\n> > > > > +     Quantized &operator=(float x)\n> > > > > +     {\n> > > > > +             quantized_ = Traits::fromFloat(x);\n> > > > > +             value_ = Traits::toFloat(quantized_);\n> > > > \n> > > > I think I have brought this up earlier, but I'm wondering if we might want to\n> > > > store `x` in `value_`. For example, if a control has float type, is it expected\n> > > > that the application should account for the quantization when comparing the\n> > > > desired value and actual value in the request metadata to check for convergence?\n> > \n> > Metadata should report the exact value applied to the frame, so I think\n> > it should be up to the application to deal with the fact they will\n> > receive rounded/quantized values.\n> > \n> > There's however another use case for the float value. Within libcamera,\n> > we often need to check if control values set in a request have changed\n> > compared to the previous requests. Comparing the float value will fail.\n> > Is it envisioned that this would be done by first converting the float\n> > value to a Quantized instance, and then comparing the Quantized\n> > instances ? If so, I think we should consider making the foat (and\n> > possibly QuantizedType) constructors explicit, to avoid unexpected\n> > behaviour when comparing Quantized and float.\n> \n> I don't yet understand why you need explicit constructors for this, or\n> what specifically your asking to (remove?)\n> \n> Later in the series shows where this exact use case is added:\n> \n> \t/* Only process if the control is in the request */\n> \tconst auto &hue = controls.get(controls::Hue);\n> \tif (hue) {\n> \t\t/* Immediately quantize the incoming value */\n> \t\tHueQ value = *hue;\n> \t\t/* This compares *quantized* values, and cproc.hue is a\n> \t\t * quantized type so this compares the quantized not the\n> \t\t * float correctly.*/\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> \n> Is there something specific you're concerned about or would like to\n> inhibit ?\n\nThe above is fine. What I'd like to avoid is\n\n \tconst auto &hue = controls.get(controls::Hue);\n \tif (hue) {\n \t\t/*\n\t\t * This implicitly creates a HueQ instance from *hue (a float)\n\t\t * and then compares Quantized instances.\n\t\t */\n \t\tif (cproc.hue != *hue) {\n\t\t\t...\n \t\t}\n\n\t\t...\n \t}\n\nMaking the constructor explicit will ensure implicit conversion won't\noccur.\n\nOr do you think the above construct should be allowed ?\n\n> > > > > +             return *this;\n> > > > > +     }\n> > > > > +\n> > > > > +     Quantized &operator=(QuantizedType x)\n> > > > > +     {\n> > > > > +             value_ = Traits::toFloat(x);\n> > > > > +             quantized_ = x;\n> > > > > +             return *this;\n> > > > > +     }\n> > > > > +\n> > > > > +     float value() const noexcept { return value_; }\n> > > > > +     QuantizedType quantized() const noexcept { return quantized_; }\n> > > > > +\n> > > > > +     std::string toString() const\n> > > > > +     {\n> > > > > +             std::ostringstream oss;\n> > > > > +\n> > > > > +             oss << \"[\" << utils::hex(quantized_)\n> > > > > +                 << \":\" << value_ << \"]\";\n> > > > > +\n> > > > > +             return oss.str();\n> > > > > +     }\n> > > > \n> > > > I'd really like to have `operator<<()` and then `toString()` implemented using that.\n> > > \n> > > I'm sure in some earlier iteration I tried to add operator<<() and I\n> > > struggled, but now things have changed a lot I can indeed try again and\n> > > let you know if I struggle again.\n> > \n> > If you run into problems let me know. I've had a look at operator<<()\n> > issues when reviewing your hex() series and researched ADL\n> > (https://en.cppreference.com/w/cpp/language/adl.html) issues.\n> > \n> > > > Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n> > > > \n> > > > > +\n> > > > > +     bool operator==(const Quantized &other) const noexcept\n> > > > > +     {\n> > > > > +             return quantized_ == other.quantized_;\n> > > > > +     }\n> > > > > +\n> > > > > +     bool operator!=(const Quantized &other) const noexcept\n> > > > > +     {\n> > > > > +             return !(*this == other);\n> > > > > +     }\n> > > > > +\n> > > > > +private:\n> > > > > +     QuantizedType quantized_;\n> > > > > +     float value_;\n> > > > > +};\n> > > > > +\n> > > > > +} /* namespace ipa */\n> > > > > +\n> > > > > +} /* namespace libcamera */","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 27B28C0F1B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 19 Nov 2025 05:13:08 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1939B60A80;\n\tWed, 19 Nov 2025 06:13:07 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A491960856\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 19 Nov 2025 06:13:05 +0100 (CET)","from pendragon.ideasonboard.com (unknown [205.220.129.225])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 47ADF14B0; \n\tWed, 19 Nov 2025 06:10:58 +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=\"Tr/TrSV6\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763529060;\n\tbh=RDOQjRgtRZWKtlM9snsXGlk4FxIhgnWZyQSORETqIJg=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=Tr/TrSV66Ne/q5qXA96SWqYKDu2T0R/7hz45LmCZy3tFegWucmbMIYGJ/wD/bERAj\n\tdNGIlsK7QMOaF1+ZzJEwDcQQv0QlsPLV6zuYw8TrRALRE+smLa63y0LLD5bixL5vUe\n\tUS5gLRMMbIsgoDW7FojyL6s/97NvV4kSyHxWQnU4=","Date":"Wed, 19 Nov 2025 14:12:36 +0900","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","Message-ID":"<20251119051236.GZ10711@pendragon.ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-2-kieran.bingham@ideasonboard.com>\n\t<3aeba3dd-d44f-4436-b806-3d79b2095117@ideasonboard.com>\n\t<176314645440.567526.14469539936828264534@ping.linuxembedded.co.uk>\n\t<20251117124838.GA17915@pendragon.ideasonboard.com>\n\t<176350708304.3013501.15656843421587014881@ping.linuxembedded.co.uk>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<176350708304.3013501.15656843421587014881@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>"}},{"id":36913,"web_url":"https://patchwork.libcamera.org/comment/36913/","msgid":"<176354482896.567526.18317928837957365766@ping.linuxembedded.co.uk>","date":"2025-11-19T09:33:48","subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","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 05:12:36)\n> On Tue, Nov 18, 2025 at 11:04:43PM +0000, Kieran Bingham wrote:\n> > Quoting Laurent Pinchart (2025-11-17 12:48:38)\n> > > On Fri, Nov 14, 2025 at 06:54:14PM +0000, Kieran Bingham wrote:\n> > > > Quoting Barnabás Pőcze (2025-11-14 18:08:03)\n> > > > > 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta:\n> > > > > > Frequently when handling data in IPA components we must convert and\n> > > > > > store user interface values which may be floating point values, and\n> > > > > > perform a specific operation or conversion to quantize this to a\n> > > > > > hardware value.\n> > > > > > \n> > > > > > This value may be to a fixed point type, or more custom code mappings,\n> > > > > > but in either case it is important to contain both the required hardware\n> > > > > > value, with its effective quantized value.\n> > > > > > \n> > > > > > Provide a new storage type 'Quantized' which can be defined based on a\n> > > > > > set of type specific Traits to perform the conversions between floats\n> > > > > > and the underlying hardware type.\n> > > > > > \n> > > > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n<snip>\n\n> > > > > > +     Quantized &operator=(float x)\n> > > > > > +     {\n> > > > > > +             quantized_ = Traits::fromFloat(x);\n> > > > > > +             value_ = Traits::toFloat(quantized_);\n> > > > > \n> > > > > I think I have brought this up earlier, but I'm wondering if we might want to\n> > > > > store `x` in `value_`. For example, if a control has float type, is it expected\n> > > > > that the application should account for the quantization when comparing the\n> > > > > desired value and actual value in the request metadata to check for convergence?\n> > > \n> > > Metadata should report the exact value applied to the frame, so I think\n> > > it should be up to the application to deal with the fact they will\n> > > receive rounded/quantized values.\n> > > \n> > > There's however another use case for the float value. Within libcamera,\n> > > we often need to check if control values set in a request have changed\n> > > compared to the previous requests. Comparing the float value will fail.\n> > > Is it envisioned that this would be done by first converting the float\n> > > value to a Quantized instance, and then comparing the Quantized\n> > > instances ? If so, I think we should consider making the foat (and\n> > > possibly QuantizedType) constructors explicit, to avoid unexpected\n> > > behaviour when comparing Quantized and float.\n> > \n> > I don't yet understand why you need explicit constructors for this, or\n> > what specifically your asking to (remove?)\n> > \n> > Later in the series shows where this exact use case is added:\n> > \n> >       /* Only process if the control is in the request */\n> >       const auto &hue = controls.get(controls::Hue);\n> >       if (hue) {\n> >               /* Immediately quantize the incoming value */\n> >               HueQ value = *hue;\n> >               /* This compares *quantized* values, and cproc.hue is a\n> >                * quantized type so this compares the quantized not the\n> >                * float correctly.*/\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> > \n> > Is there something specific you're concerned about or would like to\n> > inhibit ?\n> \n> The above is fine. What I'd like to avoid is\n> \n>         const auto &hue = controls.get(controls::Hue);\n>         if (hue) {\n>                 /*\n>                  * This implicitly creates a HueQ instance from *hue (a float)\n>                  * and then compares Quantized instances.\n>                  */\n>                 if (cproc.hue != *hue) {\n>                         ...\n>                 }\n> \n>                 ...\n>         }\n> \n> Making the constructor explicit will ensure implicit conversion won't\n> occur.\n> \n> Or do you think the above construct should be allowed ?\n\nIn fact - I think the above is equivalent and valid just like the use\ncase I had.\n\n*hue will be implicitly converted to a HueQ and quantized, and only the\nquantized value will be compared.\n\nIff that value is different, it will then update the cproc hardware.\n\nSo in your case, the implementation is:\n\nconst auto &hue = controls.get(controls::Hue);\nif (hue) {\n\t/*\n\t * This implicitly creates a HueQ instance from *hue (a float)\n\t * and then compares Quantized instances.\n\t */\n\tif (cproc.hue != *hue) {\n\t\tcproc.hue = *hue; /* Duplicated quantisation */\n\t\tupdate = true;\n\t\t/*\n\t\t * Use the new operator<< to output:\n\t\t *  \"Set hue to [0x80:-1]\"\n\t\t */\n\t\tLOG(RkISP1CProc, Debug) << \"Set hue to \" << cproc.hue;\n\t}\n}\n\n--\nKieran\n\n> \n> > > > > > +             return *this;\n> > > > > > +     }\n> > > > > > +\n> > > > > > +     Quantized &operator=(QuantizedType x)\n> > > > > > +     {\n> > > > > > +             value_ = Traits::toFloat(x);\n> > > > > > +             quantized_ = x;\n> > > > > > +             return *this;\n> > > > > > +     }\n> > > > > > +\n> > > > > > +     float value() const noexcept { return value_; }\n> > > > > > +     QuantizedType quantized() const noexcept { return quantized_; }\n> > > > > > +\n> > > > > > +     std::string toString() const\n> > > > > > +     {\n> > > > > > +             std::ostringstream oss;\n> > > > > > +\n> > > > > > +             oss << \"[\" << utils::hex(quantized_)\n> > > > > > +                 << \":\" << value_ << \"]\";\n> > > > > > +\n> > > > > > +             return oss.str();\n> > > > > > +     }\n> > > > > \n> > > > > I'd really like to have `operator<<()` and then `toString()` implemented using that.\n> > > > \n> > > > I'm sure in some earlier iteration I tried to add operator<<() and I\n> > > > struggled, but now things have changed a lot I can indeed try again and\n> > > > let you know if I struggle again.\n> > > \n> > > If you run into problems let me know. I've had a look at operator<<()\n> > > issues when reviewing your hex() series and researched ADL\n> > > (https://en.cppreference.com/w/cpp/language/adl.html) issues.\n\n\nI'll dig into this first, but come back to you if I can't get it solved.\n\n--\nThanks\nKieran\n\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 EB43AC0F1B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 19 Nov 2025 09:33:54 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 255F760A8B;\n\tWed, 19 Nov 2025 10:33:54 +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 3DFD460805\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 19 Nov 2025 10:33:52 +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 BC606E7C;\n\tWed, 19 Nov 2025 10:31:47 +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=\"EfYhzBu+\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763544707;\n\tbh=pj5qDD4d/MqYCteXE10cQxtp8T5isM4bLQZbJCumYnI=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=EfYhzBu+j6rru4b/V8z4fWvYL+ZMG9FHJ6VfFUiEePZWw8ZhxpoPwAUnjR6+f1L/2\n\tfipi4fT4kiKo2wJxVGPyRGiK54LKOXPMEkgY9EYwC7UYreQwaxHhQhdTwvmfYdIZyv\n\tQqYWvEmMsinhCQDdLnemapuKhtK+XF0OoR/8E4i0=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20251119051236.GZ10711@pendragon.ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-2-kieran.bingham@ideasonboard.com>\n\t<3aeba3dd-d44f-4436-b806-3d79b2095117@ideasonboard.com>\n\t<176314645440.567526.14469539936828264534@ping.linuxembedded.co.uk>\n\t<20251117124838.GA17915@pendragon.ideasonboard.com>\n\t<176350708304.3013501.15656843421587014881@ping.linuxembedded.co.uk>\n\t<20251119051236.GZ10711@pendragon.ideasonboard.com>","Subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Date":"Wed, 19 Nov 2025 09:33:48 +0000","Message-ID":"<176354482896.567526.18317928837957365766@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":36927,"web_url":"https://patchwork.libcamera.org/comment/36927/","msgid":"<20251120014752.GF10711@pendragon.ideasonboard.com>","date":"2025-11-20T01:47:52","subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Wed, Nov 19, 2025 at 09:33:48AM +0000, Kieran Bingham wrote:\n> Quoting Laurent Pinchart (2025-11-19 05:12:36)\n> > On Tue, Nov 18, 2025 at 11:04:43PM +0000, Kieran Bingham wrote:\n> > > Quoting Laurent Pinchart (2025-11-17 12:48:38)\n> > > > On Fri, Nov 14, 2025 at 06:54:14PM +0000, Kieran Bingham wrote:\n> > > > > Quoting Barnabás Pőcze (2025-11-14 18:08:03)\n> > > > > > 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta:\n> > > > > > > Frequently when handling data in IPA components we must convert and\n> > > > > > > store user interface values which may be floating point values, and\n> > > > > > > perform a specific operation or conversion to quantize this to a\n> > > > > > > hardware value.\n> > > > > > > \n> > > > > > > This value may be to a fixed point type, or more custom code mappings,\n> > > > > > > but in either case it is important to contain both the required hardware\n> > > > > > > value, with its effective quantized value.\n> > > > > > > \n> > > > > > > Provide a new storage type 'Quantized' which can be defined based on a\n> > > > > > > set of type specific Traits to perform the conversions between floats\n> > > > > > > and the underlying hardware type.\n> > > > > > > \n> > > > > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> <snip>\n> \n> > > > > > > +     Quantized &operator=(float x)\n> > > > > > > +     {\n> > > > > > > +             quantized_ = Traits::fromFloat(x);\n> > > > > > > +             value_ = Traits::toFloat(quantized_);\n> > > > > > \n> > > > > > I think I have brought this up earlier, but I'm wondering if we might want to\n> > > > > > store `x` in `value_`. For example, if a control has float type, is it expected\n> > > > > > that the application should account for the quantization when comparing the\n> > > > > > desired value and actual value in the request metadata to check for convergence?\n> > > > \n> > > > Metadata should report the exact value applied to the frame, so I think\n> > > > it should be up to the application to deal with the fact they will\n> > > > receive rounded/quantized values.\n> > > > \n> > > > There's however another use case for the float value. Within libcamera,\n> > > > we often need to check if control values set in a request have changed\n> > > > compared to the previous requests. Comparing the float value will fail.\n> > > > Is it envisioned that this would be done by first converting the float\n> > > > value to a Quantized instance, and then comparing the Quantized\n> > > > instances ? If so, I think we should consider making the foat (and\n> > > > possibly QuantizedType) constructors explicit, to avoid unexpected\n> > > > behaviour when comparing Quantized and float.\n> > > \n> > > I don't yet understand why you need explicit constructors for this, or\n> > > what specifically your asking to (remove?)\n> > > \n> > > Later in the series shows where this exact use case is added:\n> > > \n> > >       /* Only process if the control is in the request */\n> > >       const auto &hue = controls.get(controls::Hue);\n> > >       if (hue) {\n> > >               /* Immediately quantize the incoming value */\n> > >               HueQ value = *hue;\n> > >               /* This compares *quantized* values, and cproc.hue is a\n> > >                * quantized type so this compares the quantized not the\n> > >                * float correctly.*/\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> > > \n> > > Is there something specific you're concerned about or would like to\n> > > inhibit ?\n> > \n> > The above is fine. What I'd like to avoid is\n> > \n> >         const auto &hue = controls.get(controls::Hue);\n> >         if (hue) {\n> >                 /*\n> >                  * This implicitly creates a HueQ instance from *hue (a float)\n> >                  * and then compares Quantized instances.\n> >                  */\n> >                 if (cproc.hue != *hue) {\n> >                         ...\n> >                 }\n> > \n> >                 ...\n> >         }\n> > \n> > Making the constructor explicit will ensure implicit conversion won't\n> > occur.\n> > \n> > Or do you think the above construct should be allowed ?\n> \n> In fact - I think the above is equivalent and valid just like the use\n> case I had.\n\nThey are equivalent, but that's not my point. I think the code in my\nexample is confusing, as the comparison between cproc.hue and a float\nmakes it seem that float values are compared. Forcing explicit\nconversion to a Quantized type avoids that.\n\n> *hue will be implicitly converted to a HueQ and quantized, and only the\n> quantized value will be compared.\n> \n> Iff that value is different, it will then update the cproc hardware.\n> \n> So in your case, the implementation is:\n> \n> const auto &hue = controls.get(controls::Hue);\n> if (hue) {\n> \t/*\n> \t * This implicitly creates a HueQ instance from *hue (a float)\n> \t * and then compares Quantized instances.\n> \t */\n> \tif (cproc.hue != *hue) {\n> \t\tcproc.hue = *hue; /* Duplicated quantisation */\n> \t\tupdate = true;\n> \t\t/*\n> \t\t * Use the new operator<< to output:\n> \t\t *  \"Set hue to [0x80:-1]\"\n> \t\t */\n> \t\tLOG(RkISP1CProc, Debug) << \"Set hue to \" << cproc.hue;\n> \t}\n> }\n> \n> > > > > > > +             return *this;\n> > > > > > > +     }\n> > > > > > > +\n> > > > > > > +     Quantized &operator=(QuantizedType x)\n> > > > > > > +     {\n> > > > > > > +             value_ = Traits::toFloat(x);\n> > > > > > > +             quantized_ = x;\n> > > > > > > +             return *this;\n> > > > > > > +     }\n> > > > > > > +\n> > > > > > > +     float value() const noexcept { return value_; }\n> > > > > > > +     QuantizedType quantized() const noexcept { return quantized_; }\n> > > > > > > +\n> > > > > > > +     std::string toString() const\n> > > > > > > +     {\n> > > > > > > +             std::ostringstream oss;\n> > > > > > > +\n> > > > > > > +             oss << \"[\" << utils::hex(quantized_)\n> > > > > > > +                 << \":\" << value_ << \"]\";\n> > > > > > > +\n> > > > > > > +             return oss.str();\n> > > > > > > +     }\n> > > > > > \n> > > > > > I'd really like to have `operator<<()` and then `toString()` implemented using that.\n> > > > > \n> > > > > I'm sure in some earlier iteration I tried to add operator<<() and I\n> > > > > struggled, but now things have changed a lot I can indeed try again and\n> > > > > let you know if I struggle again.\n> > > > \n> > > > If you run into problems let me know. I've had a look at operator<<()\n> > > > issues when reviewing your hex() series and researched ADL\n> > > > (https://en.cppreference.com/w/cpp/language/adl.html) issues.\n> \n> I'll dig into this first, but come back to you if I can't get it solved.","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 2E389C0F1B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 20 Nov 2025 01:48:18 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 42D8660A8B;\n\tThu, 20 Nov 2025 02:48:17 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id E4336606D5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 20 Nov 2025 02:48:14 +0100 (CET)","from pendragon.ideasonboard.com (fs276ed015.tkyc509.ap.nuro.jp\n\t[39.110.208.21])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 02F7EC77;\n\tThu, 20 Nov 2025 02:46:08 +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=\"mrdRjMvJ\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1763603169;\n\tbh=OAGpyPV05Pql3EBS9H24gunbNPZn6LpXgshe1wxmqvQ=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=mrdRjMvJ4bHU+zkmkXtuFQJ313yRqQINjVmHE2NZ+vGR9GXtUJC6rIWJJadhX3d6C\n\tYIRXlNoBnSeVz5R+eEMdzmfHktlZLwbECZ7Vl7DFlfsU+dQz/I5byq1nDLSBwLJpZu\n\tyvyIwMPut24jzUASeDj/AM0NEwNIchlv8O19wMJY=","Date":"Thu, 20 Nov 2025 10:47:52 +0900","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","Message-ID":"<20251120014752.GF10711@pendragon.ideasonboard.com>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-2-kieran.bingham@ideasonboard.com>\n\t<3aeba3dd-d44f-4436-b806-3d79b2095117@ideasonboard.com>\n\t<176314645440.567526.14469539936828264534@ping.linuxembedded.co.uk>\n\t<20251117124838.GA17915@pendragon.ideasonboard.com>\n\t<176350708304.3013501.15656843421587014881@ping.linuxembedded.co.uk>\n\t<20251119051236.GZ10711@pendragon.ideasonboard.com>\n\t<176354482896.567526.18317928837957365766@ping.linuxembedded.co.uk>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<176354482896.567526.18317928837957365766@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>"}},{"id":37925,"web_url":"https://patchwork.libcamera.org/comment/37925/","msgid":"<20260124010901.GA438854@killaraus>","date":"2026-01-24T01:09:01","subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Thu, Nov 20, 2025 at 10:47:52AM +0900, Laurent Pinchart wrote:\n> On Wed, Nov 19, 2025 at 09:33:48AM +0000, Kieran Bingham wrote:\n> > Quoting Laurent Pinchart (2025-11-19 05:12:36)\n> > > On Tue, Nov 18, 2025 at 11:04:43PM +0000, Kieran Bingham wrote:\n> > > > Quoting Laurent Pinchart (2025-11-17 12:48:38)\n> > > > > On Fri, Nov 14, 2025 at 06:54:14PM +0000, Kieran Bingham wrote:\n> > > > > > Quoting Barnabás Pőcze (2025-11-14 18:08:03)\n> > > > > > > 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta:\n> > > > > > > > Frequently when handling data in IPA components we must convert and\n> > > > > > > > store user interface values which may be floating point values, and\n> > > > > > > > perform a specific operation or conversion to quantize this to a\n> > > > > > > > hardware value.\n> > > > > > > > \n> > > > > > > > This value may be to a fixed point type, or more custom code mappings,\n> > > > > > > > but in either case it is important to contain both the required hardware\n> > > > > > > > value, with its effective quantized value.\n> > > > > > > > \n> > > > > > > > Provide a new storage type 'Quantized' which can be defined based on a\n> > > > > > > > set of type specific Traits to perform the conversions between floats\n> > > > > > > > and the underlying hardware type.\n> > > > > > > > \n> > > > > > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > \n> > <snip>\n> > \n> > > > > > > > +     Quantized &operator=(float x)\n> > > > > > > > +     {\n> > > > > > > > +             quantized_ = Traits::fromFloat(x);\n> > > > > > > > +             value_ = Traits::toFloat(quantized_);\n> > > > > > > \n> > > > > > > I think I have brought this up earlier, but I'm wondering if we might want to\n> > > > > > > store `x` in `value_`. For example, if a control has float type, is it expected\n> > > > > > > that the application should account for the quantization when comparing the\n> > > > > > > desired value and actual value in the request metadata to check for convergence?\n> > > > > \n> > > > > Metadata should report the exact value applied to the frame, so I think\n> > > > > it should be up to the application to deal with the fact they will\n> > > > > receive rounded/quantized values.\n> > > > > \n> > > > > There's however another use case for the float value. Within libcamera,\n> > > > > we often need to check if control values set in a request have changed\n> > > > > compared to the previous requests. Comparing the float value will fail.\n> > > > > Is it envisioned that this would be done by first converting the float\n> > > > > value to a Quantized instance, and then comparing the Quantized\n> > > > > instances ? If so, I think we should consider making the foat (and\n> > > > > possibly QuantizedType) constructors explicit, to avoid unexpected\n> > > > > behaviour when comparing Quantized and float.\n> > > > \n> > > > I don't yet understand why you need explicit constructors for this, or\n> > > > what specifically your asking to (remove?)\n> > > > \n> > > > Later in the series shows where this exact use case is added:\n> > > > \n> > > >       /* Only process if the control is in the request */\n> > > >       const auto &hue = controls.get(controls::Hue);\n> > > >       if (hue) {\n> > > >               /* Immediately quantize the incoming value */\n> > > >               HueQ value = *hue;\n> > > >               /* This compares *quantized* values, and cproc.hue is a\n> > > >                * quantized type so this compares the quantized not the\n> > > >                * float correctly.*/\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> > > > \n> > > > Is there something specific you're concerned about or would like to\n> > > > inhibit ?\n> > > \n> > > The above is fine. What I'd like to avoid is\n> > > \n> > >         const auto &hue = controls.get(controls::Hue);\n> > >         if (hue) {\n> > >                 /*\n> > >                  * This implicitly creates a HueQ instance from *hue (a float)\n> > >                  * and then compares Quantized instances.\n> > >                  */\n> > >                 if (cproc.hue != *hue) {\n> > >                         ...\n> > >                 }\n> > > \n> > >                 ...\n> > >         }\n> > > \n> > > Making the constructor explicit will ensure implicit conversion won't\n> > > occur.\n> > > \n> > > Or do you think the above construct should be allowed ?\n> > \n> > In fact - I think the above is equivalent and valid just like the use\n> > case I had.\n> \n> They are equivalent, but that's not my point. I think the code in my\n> example is confusing, as the comparison between cproc.hue and a float\n> makes it seem that float values are compared. Forcing explicit\n> conversion to a Quantized type avoids that.\n\nWhile reviewing v6 I noticed that this conversation died. Kieran,\nBarnabás, any opinion ?\n\n> > *hue will be implicitly converted to a HueQ and quantized, and only the\n> > quantized value will be compared.\n> > \n> > Iff that value is different, it will then update the cproc hardware.\n> > \n> > So in your case, the implementation is:\n> > \n> > const auto &hue = controls.get(controls::Hue);\n> > if (hue) {\n> > \t/*\n> > \t * This implicitly creates a HueQ instance from *hue (a float)\n> > \t * and then compares Quantized instances.\n> > \t */\n> > \tif (cproc.hue != *hue) {\n> > \t\tcproc.hue = *hue; /* Duplicated quantisation */\n> > \t\tupdate = true;\n> > \t\t/*\n> > \t\t * Use the new operator<< to output:\n> > \t\t *  \"Set hue to [0x80:-1]\"\n> > \t\t */\n> > \t\tLOG(RkISP1CProc, Debug) << \"Set hue to \" << cproc.hue;\n> > \t}\n> > }\n> > \n> > > > > > > > +             return *this;\n> > > > > > > > +     }\n> > > > > > > > +\n> > > > > > > > +     Quantized &operator=(QuantizedType x)\n> > > > > > > > +     {\n> > > > > > > > +             value_ = Traits::toFloat(x);\n> > > > > > > > +             quantized_ = x;\n> > > > > > > > +             return *this;\n> > > > > > > > +     }\n> > > > > > > > +\n> > > > > > > > +     float value() const noexcept { return value_; }\n> > > > > > > > +     QuantizedType quantized() const noexcept { return quantized_; }\n> > > > > > > > +\n> > > > > > > > +     std::string toString() const\n> > > > > > > > +     {\n> > > > > > > > +             std::ostringstream oss;\n> > > > > > > > +\n> > > > > > > > +             oss << \"[\" << utils::hex(quantized_)\n> > > > > > > > +                 << \":\" << value_ << \"]\";\n> > > > > > > > +\n> > > > > > > > +             return oss.str();\n> > > > > > > > +     }\n> > > > > > > \n> > > > > > > I'd really like to have `operator<<()` and then `toString()` implemented using that.\n> > > > > > \n> > > > > > I'm sure in some earlier iteration I tried to add operator<<() and I\n> > > > > > struggled, but now things have changed a lot I can indeed try again and\n> > > > > > let you know if I struggle again.\n> > > > > \n> > > > > If you run into problems let me know. I've had a look at operator<<()\n> > > > > issues when reviewing your hex() series and researched ADL\n> > > > > (https://en.cppreference.com/w/cpp/language/adl.html) issues.\n> > \n> > I'll dig into this first, but come back to you if I can't get it solved.","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 2F633C3220\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 24 Jan 2026 01:09:06 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 36F6461FC9;\n\tSat, 24 Jan 2026 02:09:05 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D6E1361F84\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 24 Jan 2026 02:09:02 +0100 (CET)","from pendragon.ideasonboard.com\n\t(2001-14ba-703d-e500--2a1.rev.dnainternet.fi\n\t[IPv6:2001:14ba:703d:e500::2a1])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id E5835448;\n\tSat, 24 Jan 2026 02:08:28 +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=\"dH5QzKO4\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1769216909;\n\tbh=BJx7JeMSDqUfsBbisyFzg4m+0bHglKadLC124weV/tM=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=dH5QzKO4q48mAmp+XSav1FJ1TyHySZXexsdwF9//S5xFk5O7jqMhtICcWikQ3vgOO\n\t+fXCtfs3gE59psuQzsacrnqCrp6djNDTwkgWY5K7b7RGHFdXG653Iv/Qaq+QWlg108\n\t2G4RNtSMyFyKlu4U2VzI3wcMLQQxgmuIbgskRmBw=","Date":"Sat, 24 Jan 2026 03:09:01 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","Message-ID":"<20260124010901.GA438854@killaraus>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-2-kieran.bingham@ideasonboard.com>\n\t<3aeba3dd-d44f-4436-b806-3d79b2095117@ideasonboard.com>\n\t<176314645440.567526.14469539936828264534@ping.linuxembedded.co.uk>\n\t<20251117124838.GA17915@pendragon.ideasonboard.com>\n\t<176350708304.3013501.15656843421587014881@ping.linuxembedded.co.uk>\n\t<20251119051236.GZ10711@pendragon.ideasonboard.com>\n\t<176354482896.567526.18317928837957365766@ping.linuxembedded.co.uk>\n\t<20251120014752.GF10711@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20251120014752.GF10711@pendragon.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":37944,"web_url":"https://patchwork.libcamera.org/comment/37944/","msgid":"<84d29c41-e85f-4eb8-a000-96ce82ad948a@ideasonboard.com>","date":"2026-01-26T14:09:52","subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"2026. 01. 24. 2:09 keltezéssel, Laurent Pinchart írta:\n> On Thu, Nov 20, 2025 at 10:47:52AM +0900, Laurent Pinchart wrote:\n>> On Wed, Nov 19, 2025 at 09:33:48AM +0000, Kieran Bingham wrote:\n>>> Quoting Laurent Pinchart (2025-11-19 05:12:36)\n>>>> On Tue, Nov 18, 2025 at 11:04:43PM +0000, Kieran Bingham wrote:\n>>>>> Quoting Laurent Pinchart (2025-11-17 12:48:38)\n>>>>>> On Fri, Nov 14, 2025 at 06:54:14PM +0000, Kieran Bingham wrote:\n>>>>>>> Quoting Barnabás Pőcze (2025-11-14 18:08:03)\n>>>>>>>> 2025. 11. 14. 1:54 keltezéssel, Kieran Bingham írta:\n>>>>>>>>> Frequently when handling data in IPA components we must convert and\n>>>>>>>>> store user interface values which may be floating point values, and\n>>>>>>>>> perform a specific operation or conversion to quantize this to a\n>>>>>>>>> hardware value.\n>>>>>>>>>\n>>>>>>>>> This value may be to a fixed point type, or more custom code mappings,\n>>>>>>>>> but in either case it is important to contain both the required hardware\n>>>>>>>>> value, with its effective quantized value.\n>>>>>>>>>\n>>>>>>>>> Provide a new storage type 'Quantized' which can be defined based on a\n>>>>>>>>> set of type specific Traits to perform the conversions between floats\n>>>>>>>>> and the underlying hardware type.\n>>>>>>>>>\n>>>>>>>>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n>>>\n>>> <snip>\n>>>\n>>>>>>>>> +     Quantized &operator=(float x)\n>>>>>>>>> +     {\n>>>>>>>>> +             quantized_ = Traits::fromFloat(x);\n>>>>>>>>> +             value_ = Traits::toFloat(quantized_);\n>>>>>>>>\n>>>>>>>> I think I have brought this up earlier, but I'm wondering if we might want to\n>>>>>>>> store `x` in `value_`. For example, if a control has float type, is it expected\n>>>>>>>> that the application should account for the quantization when comparing the\n>>>>>>>> desired value and actual value in the request metadata to check for convergence?\n>>>>>>\n>>>>>> Metadata should report the exact value applied to the frame, so I think\n>>>>>> it should be up to the application to deal with the fact they will\n>>>>>> receive rounded/quantized values.\n>>>>>>\n>>>>>> There's however another use case for the float value. Within libcamera,\n>>>>>> we often need to check if control values set in a request have changed\n>>>>>> compared to the previous requests. Comparing the float value will fail.\n>>>>>> Is it envisioned that this would be done by first converting the float\n>>>>>> value to a Quantized instance, and then comparing the Quantized\n>>>>>> instances ? If so, I think we should consider making the foat (and\n>>>>>> possibly QuantizedType) constructors explicit, to avoid unexpected\n>>>>>> behaviour when comparing Quantized and float.\n>>>>>\n>>>>> I don't yet understand why you need explicit constructors for this, or\n>>>>> what specifically your asking to (remove?)\n>>>>>\n>>>>> Later in the series shows where this exact use case is added:\n>>>>>\n>>>>>        /* Only process if the control is in the request */\n>>>>>        const auto &hue = controls.get(controls::Hue);\n>>>>>        if (hue) {\n>>>>>                /* Immediately quantize the incoming value */\n>>>>>                HueQ value = *hue;\n>>>>>                /* This compares *quantized* values, and cproc.hue is a\n>>>>>                 * quantized type so this compares the quantized not the\n>>>>>                 * float correctly.*/\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>>>>>\n>>>>> Is there something specific you're concerned about or would like to\n>>>>> inhibit ?\n>>>>\n>>>> The above is fine. What I'd like to avoid is\n>>>>\n>>>>          const auto &hue = controls.get(controls::Hue);\n>>>>          if (hue) {\n>>>>                  /*\n>>>>                   * This implicitly creates a HueQ instance from *hue (a float)\n>>>>                   * and then compares Quantized instances.\n>>>>                   */\n>>>>                  if (cproc.hue != *hue) {\n>>>>                          ...\n>>>>                  }\n>>>>\n>>>>                  ...\n>>>>          }\n>>>>\n>>>> Making the constructor explicit will ensure implicit conversion won't\n>>>> occur.\n>>>>\n>>>> Or do you think the above construct should be allowed ?\n>>>\n>>> In fact - I think the above is equivalent and valid just like the use\n>>> case I had.\n>>\n>> They are equivalent, but that's not my point. I think the code in my\n>> example is confusing, as the comparison between cproc.hue and a float\n>> makes it seem that float values are compared. Forcing explicit\n>> conversion to a Quantized type avoids that.\n> \n> While reviewing v6 I noticed that this conversation died. Kieran,\n> Barnabás, any opinion ?\n\nI don't have a strong opinion either way. I suppose it depends mainly on\none's expectations about the result of a `Quantized` vs `float` comparison.\n\nIf one expects comparison of the floating point values, then they will be\nsurprised; on the other hand, if one expects an answer to the question \"does\nthis `float` value correspond to this quantized value?\", then the code does\nwhat they want.\n\nOf course going with `explicit` is \"safer\" in the sense that there are no\nimplicit conversions, so there are no surprises. Making double quantization\nmore difficult to do accidentally might also be an argument for `explicit`.\n\n\n> \n>>> *hue will be implicitly converted to a HueQ and quantized, and only the\n>>> quantized value will be compared.\n>>>\n>>> Iff that value is different, it will then update the cproc hardware.\n>>>\n>>> So in your case, the implementation is:\n>>>\n>>> const auto &hue = controls.get(controls::Hue);\n>>> if (hue) {\n>>> \t/*\n>>> \t * This implicitly creates a HueQ instance from *hue (a float)\n>>> \t * and then compares Quantized instances.\n>>> \t */\n>>> \tif (cproc.hue != *hue) {\n>>> \t\tcproc.hue = *hue; /* Duplicated quantisation */\n>>> \t\tupdate = true;\n>>> \t\t/*\n>>> \t\t * Use the new operator<< to output:\n>>> \t\t *  \"Set hue to [0x80:-1]\"\n>>> \t\t */\n>>> \t\tLOG(RkISP1CProc, Debug) << \"Set hue to \" << cproc.hue;\n>>> \t}\n>>> }\n>>>\n>>>>>>>>> +             return *this;\n>>>>>>>>> +     }\n>>>>>>>>> +\n>>>>>>>>> +     Quantized &operator=(QuantizedType x)\n>>>>>>>>> +     {\n>>>>>>>>> +             value_ = Traits::toFloat(x);\n>>>>>>>>> +             quantized_ = x;\n>>>>>>>>> +             return *this;\n>>>>>>>>> +     }\n>>>>>>>>> +\n>>>>>>>>> +     float value() const noexcept { return value_; }\n>>>>>>>>> +     QuantizedType quantized() const noexcept { return quantized_; }\n>>>>>>>>> +\n>>>>>>>>> +     std::string toString() const\n>>>>>>>>> +     {\n>>>>>>>>> +             std::ostringstream oss;\n>>>>>>>>> +\n>>>>>>>>> +             oss << \"[\" << utils::hex(quantized_)\n>>>>>>>>> +                 << \":\" << value_ << \"]\";\n>>>>>>>>> +\n>>>>>>>>> +             return oss.str();\n>>>>>>>>> +     }\n>>>>>>>>\n>>>>>>>> I'd really like to have `operator<<()` and then `toString()` implemented using that.\n>>>>>>>\n>>>>>>> I'm sure in some earlier iteration I tried to add operator<<() and I\n>>>>>>> struggled, but now things have changed a lot I can indeed try again and\n>>>>>>> let you know if I struggle again.\n>>>>>>\n>>>>>> If you run into problems let me know. I've had a look at operator<<()\n>>>>>> issues when reviewing your hex() series and researched ADL\n>>>>>> (https://en.cppreference.com/w/cpp/language/adl.html) issues.\n>>>\n>>> I'll dig into this first, but come back to you if I can't get it solved.\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 5FC5AC3200\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 26 Jan 2026 14:09:59 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3EFC861FCB;\n\tMon, 26 Jan 2026 15:09:58 +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 8210261A35\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 26 Jan 2026 15:09:55 +0100 (CET)","from [192.168.33.36] (185.221.142.123.nat.pool.zt.hu\n\t[185.221.142.123])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 5F7C0557;\n\tMon, 26 Jan 2026 15:09:19 +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=\"GOb+Yfsx\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1769436559;\n\tbh=7fJ9PfQSEt4evs6pjExLiJr2wB/8sfldVNkd1HRKt4I=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=GOb+YfsxI5v4NVIxeQlv3+MM8I5YBjow/yrYXjgF8gOFBR16j/pRynpIYg5j7tx6Y\n\tYLCDOLPE1tbBMdT+2BfrtQs7Sj212Wnt/6kfhrNiOCGqjSH4R8vdCGjOggccaPuL5F\n\tnY5Sv4fszD0vwncl2geFH0lvy3fR3UNotODQTQXU=","Message-ID":"<84d29c41-e85f-4eb8-a000-96ce82ad948a@ideasonboard.com>","Date":"Mon, 26 Jan 2026 15:09:52 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v4 01/21] ipa: libipa: Provide a Quantized data type\n\tsupport","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","References":"<20251114005428.90024-1-kieran.bingham@ideasonboard.com>\n\t<20251114005428.90024-2-kieran.bingham@ideasonboard.com>\n\t<3aeba3dd-d44f-4436-b806-3d79b2095117@ideasonboard.com>\n\t<176314645440.567526.14469539936828264534@ping.linuxembedded.co.uk>\n\t<20251117124838.GA17915@pendragon.ideasonboard.com>\n\t<176350708304.3013501.15656843421587014881@ping.linuxembedded.co.uk>\n\t<20251119051236.GZ10711@pendragon.ideasonboard.com>\n\t<176354482896.567526.18317928837957365766@ping.linuxembedded.co.uk>\n\t<20251120014752.GF10711@pendragon.ideasonboard.com>\n\t<20260124010901.GA438854@killaraus>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20260124010901.GA438854@killaraus>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","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>"}}]