[libcamera-devel,3/6] libcamera: color_space: Add fromString() function
diff mbox series

Message ID 20220823174314.14881-4-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • libcamera: Add support for color spaces to rkisp1 pipeline handler
Related show

Commit Message

Laurent Pinchart Aug. 23, 2022, 5:43 p.m. UTC
Add a ColorSpace:fromString() function to parse a string into a color
space. The string can either contain the name of a well-known color
space, or four color space components separate by a '/' character.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 include/libcamera/color_space.h |   2 +
 src/libcamera/color_space.cpp   | 149 +++++++++++++++++++++++++-------
 2 files changed, 121 insertions(+), 30 deletions(-)

Comments

Umang Jain Aug. 24, 2022, 6:37 a.m. UTC | #1
Hi Laurent,

Thank you for the patch.

On 8/23/22 11:13 PM, Laurent Pinchart via libcamera-devel wrote:
> Add a ColorSpace:fromString() function to parse a string into a color
> space. The string can either contain the name of a well-known color
> space, or four color space components separate by a '/' character.

I don't see the order of four components separated by '/' - documented 
in the codebase.
Should we document the string order as well?

'''
The string representation of the colorspace separated by '/' is as follows:
     <Color primaries>/<Transfer function>/<Y'CbCr Encoding>/<Range>
'''
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
> ---
>   include/libcamera/color_space.h |   2 +
>   src/libcamera/color_space.cpp   | 149 +++++++++++++++++++++++++-------
>   2 files changed, 121 insertions(+), 30 deletions(-)
>
> diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h
> index 8030a264c66f..f493f72d2db8 100644
> --- a/include/libcamera/color_space.h
> +++ b/include/libcamera/color_space.h
> @@ -59,6 +59,8 @@ public:
>   
>   	std::string toString() const;
>   	static std::string toString(const std::optional<ColorSpace> &colorSpace);
> +
> +	static std::optional<ColorSpace> fromString(const std::string &str);
>   };
>   
>   bool operator==(const ColorSpace &lhs, const ColorSpace &rhs);
> diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp
> index 1b2dd2404452..5233626f5ae9 100644
> --- a/src/libcamera/color_space.cpp
> +++ b/src/libcamera/color_space.cpp
> @@ -12,6 +12,9 @@
>   #include <map>
>   #include <sstream>
>   #include <utility>
> +#include <vector>
> +
> +#include <libcamera/base/utils.h>
>   
>   /**
>    * \file color_space.h
> @@ -208,6 +211,44 @@ const ColorSpace ColorSpace::Rec2020 = {
>    * \brief The pixel range used with by color space
>    */
>   
> +namespace {
> +
> +const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
> +	{ ColorSpace::Raw, "RAW" },
> +	{ ColorSpace::Srgb, "sRGB" },
> +	{ ColorSpace::Sycc, "sYCC" },
> +	{ ColorSpace::Smpte170m, "SMPTE170M" },
> +	{ ColorSpace::Rec709, "Rec709" },
> +	{ ColorSpace::Rec2020, "Rec2020" },
> +} };
> +
> +const std::map<ColorSpace::Primaries, std::string> primariesNames = {
> +	{ ColorSpace::Primaries::Raw, "RAW" },
> +	{ ColorSpace::Primaries::Smpte170m, "SMPTE170M" },
> +	{ ColorSpace::Primaries::Rec709, "Rec709" },
> +	{ ColorSpace::Primaries::Rec2020, "Rec2020" },
> +};
> +
> +const std::map<ColorSpace::TransferFunction, std::string> transferNames = {
> +	{ ColorSpace::TransferFunction::Linear, "Linear" },
> +	{ ColorSpace::TransferFunction::Srgb, "sRGB" },
> +	{ ColorSpace::TransferFunction::Rec709, "Rec709" },
> +};
> +
> +const std::map<ColorSpace::YcbcrEncoding, std::string> encodingNames = {
> +	{ ColorSpace::YcbcrEncoding::None, "None" },
> +	{ ColorSpace::YcbcrEncoding::Rec601, "Rec601" },
> +	{ ColorSpace::YcbcrEncoding::Rec709, "Rec709" },
> +	{ ColorSpace::YcbcrEncoding::Rec2020, "Rec2020" },
> +};
> +
> +const std::map<ColorSpace::Range, std::string> rangeNames = {
> +	{ ColorSpace::Range::Full, "Full" },
> +	{ ColorSpace::Range::Limited, "Limited" },
> +};
> +
> +} /* namespace */
> +
>   /**
>    * \brief Assemble and return a readable string representation of the
>    * ColorSpace
> @@ -223,14 +264,6 @@ std::string ColorSpace::toString() const
>   {
>   	/* Print out a brief name only for standard color spaces. */
>   
> -	static const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
> -		{ ColorSpace::Raw, "RAW" },
> -		{ ColorSpace::Srgb, "sRGB" },
> -		{ ColorSpace::Sycc, "sYCC" },
> -		{ ColorSpace::Smpte170m, "SMPTE170M" },
> -		{ ColorSpace::Rec709, "Rec709" },
> -		{ ColorSpace::Rec2020, "Rec2020" },
> -	} };
>   	auto it = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
>   			       [this](const auto &item) {
>   				       return *this == item.first;
> @@ -240,28 +273,6 @@ std::string ColorSpace::toString() const
>   
>   	/* Assemble a name made of the constituent fields. */
>   
> -	static const std::map<Primaries, std::string> primariesNames = {
> -		{ Primaries::Raw, "RAW" },
> -		{ Primaries::Smpte170m, "SMPTE170M" },
> -		{ Primaries::Rec709, "Rec709" },
> -		{ Primaries::Rec2020, "Rec2020" },
> -	};
> -	static const std::map<TransferFunction, std::string> transferNames = {
> -		{ TransferFunction::Linear, "Linear" },
> -		{ TransferFunction::Srgb, "sRGB" },
> -		{ TransferFunction::Rec709, "Rec709" },
> -	};
> -	static const std::map<YcbcrEncoding, std::string> encodingNames = {
> -		{ YcbcrEncoding::None, "None" },
> -		{ YcbcrEncoding::Rec601, "Rec601" },
> -		{ YcbcrEncoding::Rec709, "Rec709" },
> -		{ YcbcrEncoding::Rec2020, "Rec2020" },
> -	};
> -	static const std::map<Range, std::string> rangeNames = {
> -		{ Range::Full, "Full" },
> -		{ Range::Limited, "Limited" },
> -	};
> -
>   	auto itPrimaries = primariesNames.find(primaries);
>   	std::string primariesName =
>   		itPrimaries == primariesNames.end() ? "Invalid" : itPrimaries->second;
> @@ -303,6 +314,84 @@ std::string ColorSpace::toString(const std::optional<ColorSpace> &colorSpace)
>   	return colorSpace->toString();
>   }
>   
> +/**
> + * \brief Construct a color space from a string
> + * \param[in] str The string
> + *
> + * The string \a str can contain the name of a well-known color space, or be
> + * made of the four color space components separate by a '/' character. Any
> + * failure to parse the string, either because it doesn't match the expected
> + * format, or because the one of the names isn't recognized, will cause this
> + * function to return std::nullopt.
> + *
> + * \return The ColorSpace corresponding to the string, or std::nullopt if the
> + * string doesn't describe a known color space
> + */
> +std::optional<ColorSpace> ColorSpace::fromString(const std::string &str)
> +{
> +	/* First search for a standard color space name match. */
> +	auto itColorSpace = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
> +					 [&str](const auto &item) {
> +						 return str == item.second;
> +					 });
> +	if (itColorSpace != colorSpaceNames.end())
> +		return itColorSpace->first;
> +
> +	/*
> +	 * If not found, the string must contain the four color space
> +	 * components separated by a '/' character.
> +	 */
> +	const auto &split = utils::split(str, "/");
> +	std::vector<std::string> components{ split.begin(), split.end() };
> +
> +	if (components.size() != 4)
> +		return std::nullopt;
> +
> +	ColorSpace colorSpace = ColorSpace::Raw;
> +
> +	/* Color primaries */
> +	auto itPrimaries = std::find_if(primariesNames.begin(), primariesNames.end(),
> +					[&components](const auto &item) {
> +						return components[0] == item.second;
> +					});
> +	if (itPrimaries == primariesNames.end())
> +		return std::nullopt;
> +
> +	colorSpace.primaries = itPrimaries->first;
> +
> +	/* Transfer function */
> +	auto itTransfer = std::find_if(transferNames.begin(), transferNames.end(),
> +				       [&components](const auto &item) {
> +					       return components[1] == item.second;
> +				       });
> +	if (itTransfer == transferNames.end())
> +		return std::nullopt;
> +
> +	colorSpace.transferFunction = itTransfer->first;
> +
> +	/* YCbCr encoding */
> +	auto itEncoding = std::find_if(encodingNames.begin(), encodingNames.end(),
> +				       [&components](const auto &item) {
> +					       return components[2] == item.second;
> +				       });
> +	if (itEncoding == encodingNames.end())
> +		return std::nullopt;
> +
> +	colorSpace.ycbcrEncoding = itEncoding->first;
> +
> +	/* Quantization range */
> +	auto itRange = std::find_if(rangeNames.begin(), rangeNames.end(),
> +				    [&components](const auto &item) {
> +					    return components[3] == item.second;
> +				    });
> +	if (itRange == rangeNames.end())
> +		return std::nullopt;
> +
> +	colorSpace.range = itRange->first;
> +
> +	return colorSpace;
> +}
> +
>   /**
>    * \brief Compare color spaces for equality
>    * \return True if the two color spaces are identical, false otherwise
Laurent Pinchart Aug. 24, 2022, 9:40 a.m. UTC | #2
Hi Umang,

On Wed, Aug 24, 2022 at 12:07:59PM +0530, Umang Jain wrote:
> On 8/23/22 11:13 PM, Laurent Pinchart via libcamera-devel wrote:
> > Add a ColorSpace:fromString() function to parse a string into a color
> > space. The string can either contain the name of a well-known color
> > space, or four color space components separate by a '/' character.
> 
> I don't see the order of four components separated by '/' - documented 
> in the codebase.
> Should we document the string order as well?
> 
> '''
> The string representation of the colorspace separated by '/' is as follows:
>      <Color primaries>/<Transfer function>/<Y'CbCr Encoding>/<Range>
> '''

I'll expand the documentation of the function to make this more
explicit.

I'm also wondering (warning, bikeshedding ahead) if the '/'-separated
format is the best option. GStreamer uses colons to separate the
components, and only accepts numerical values for the components in that
case. I'm not aware of (but haven't looked for) alternatives.

> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
> 
> > ---
> >   include/libcamera/color_space.h |   2 +
> >   src/libcamera/color_space.cpp   | 149 +++++++++++++++++++++++++-------
> >   2 files changed, 121 insertions(+), 30 deletions(-)
> >
> > diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h
> > index 8030a264c66f..f493f72d2db8 100644
> > --- a/include/libcamera/color_space.h
> > +++ b/include/libcamera/color_space.h
> > @@ -59,6 +59,8 @@ public:
> >   
> >   	std::string toString() const;
> >   	static std::string toString(const std::optional<ColorSpace> &colorSpace);
> > +
> > +	static std::optional<ColorSpace> fromString(const std::string &str);
> >   };
> >   
> >   bool operator==(const ColorSpace &lhs, const ColorSpace &rhs);
> > diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp
> > index 1b2dd2404452..5233626f5ae9 100644
> > --- a/src/libcamera/color_space.cpp
> > +++ b/src/libcamera/color_space.cpp
> > @@ -12,6 +12,9 @@
> >   #include <map>
> >   #include <sstream>
> >   #include <utility>
> > +#include <vector>
> > +
> > +#include <libcamera/base/utils.h>
> >   
> >   /**
> >    * \file color_space.h
> > @@ -208,6 +211,44 @@ const ColorSpace ColorSpace::Rec2020 = {
> >    * \brief The pixel range used with by color space
> >    */
> >   
> > +namespace {
> > +
> > +const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
> > +	{ ColorSpace::Raw, "RAW" },
> > +	{ ColorSpace::Srgb, "sRGB" },
> > +	{ ColorSpace::Sycc, "sYCC" },
> > +	{ ColorSpace::Smpte170m, "SMPTE170M" },
> > +	{ ColorSpace::Rec709, "Rec709" },
> > +	{ ColorSpace::Rec2020, "Rec2020" },
> > +} };
> > +
> > +const std::map<ColorSpace::Primaries, std::string> primariesNames = {
> > +	{ ColorSpace::Primaries::Raw, "RAW" },
> > +	{ ColorSpace::Primaries::Smpte170m, "SMPTE170M" },
> > +	{ ColorSpace::Primaries::Rec709, "Rec709" },
> > +	{ ColorSpace::Primaries::Rec2020, "Rec2020" },
> > +};
> > +
> > +const std::map<ColorSpace::TransferFunction, std::string> transferNames = {
> > +	{ ColorSpace::TransferFunction::Linear, "Linear" },
> > +	{ ColorSpace::TransferFunction::Srgb, "sRGB" },
> > +	{ ColorSpace::TransferFunction::Rec709, "Rec709" },
> > +};
> > +
> > +const std::map<ColorSpace::YcbcrEncoding, std::string> encodingNames = {
> > +	{ ColorSpace::YcbcrEncoding::None, "None" },
> > +	{ ColorSpace::YcbcrEncoding::Rec601, "Rec601" },
> > +	{ ColorSpace::YcbcrEncoding::Rec709, "Rec709" },
> > +	{ ColorSpace::YcbcrEncoding::Rec2020, "Rec2020" },
> > +};
> > +
> > +const std::map<ColorSpace::Range, std::string> rangeNames = {
> > +	{ ColorSpace::Range::Full, "Full" },
> > +	{ ColorSpace::Range::Limited, "Limited" },
> > +};
> > +
> > +} /* namespace */
> > +
> >   /**
> >    * \brief Assemble and return a readable string representation of the
> >    * ColorSpace
> > @@ -223,14 +264,6 @@ std::string ColorSpace::toString() const
> >   {
> >   	/* Print out a brief name only for standard color spaces. */
> >   
> > -	static const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
> > -		{ ColorSpace::Raw, "RAW" },
> > -		{ ColorSpace::Srgb, "sRGB" },
> > -		{ ColorSpace::Sycc, "sYCC" },
> > -		{ ColorSpace::Smpte170m, "SMPTE170M" },
> > -		{ ColorSpace::Rec709, "Rec709" },
> > -		{ ColorSpace::Rec2020, "Rec2020" },
> > -	} };
> >   	auto it = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
> >   			       [this](const auto &item) {
> >   				       return *this == item.first;
> > @@ -240,28 +273,6 @@ std::string ColorSpace::toString() const
> >   
> >   	/* Assemble a name made of the constituent fields. */
> >   
> > -	static const std::map<Primaries, std::string> primariesNames = {
> > -		{ Primaries::Raw, "RAW" },
> > -		{ Primaries::Smpte170m, "SMPTE170M" },
> > -		{ Primaries::Rec709, "Rec709" },
> > -		{ Primaries::Rec2020, "Rec2020" },
> > -	};
> > -	static const std::map<TransferFunction, std::string> transferNames = {
> > -		{ TransferFunction::Linear, "Linear" },
> > -		{ TransferFunction::Srgb, "sRGB" },
> > -		{ TransferFunction::Rec709, "Rec709" },
> > -	};
> > -	static const std::map<YcbcrEncoding, std::string> encodingNames = {
> > -		{ YcbcrEncoding::None, "None" },
> > -		{ YcbcrEncoding::Rec601, "Rec601" },
> > -		{ YcbcrEncoding::Rec709, "Rec709" },
> > -		{ YcbcrEncoding::Rec2020, "Rec2020" },
> > -	};
> > -	static const std::map<Range, std::string> rangeNames = {
> > -		{ Range::Full, "Full" },
> > -		{ Range::Limited, "Limited" },
> > -	};
> > -
> >   	auto itPrimaries = primariesNames.find(primaries);
> >   	std::string primariesName =
> >   		itPrimaries == primariesNames.end() ? "Invalid" : itPrimaries->second;
> > @@ -303,6 +314,84 @@ std::string ColorSpace::toString(const std::optional<ColorSpace> &colorSpace)
> >   	return colorSpace->toString();
> >   }
> >   
> > +/**
> > + * \brief Construct a color space from a string
> > + * \param[in] str The string
> > + *
> > + * The string \a str can contain the name of a well-known color space, or be
> > + * made of the four color space components separate by a '/' character. Any
> > + * failure to parse the string, either because it doesn't match the expected
> > + * format, or because the one of the names isn't recognized, will cause this
> > + * function to return std::nullopt.
> > + *
> > + * \return The ColorSpace corresponding to the string, or std::nullopt if the
> > + * string doesn't describe a known color space
> > + */
> > +std::optional<ColorSpace> ColorSpace::fromString(const std::string &str)
> > +{
> > +	/* First search for a standard color space name match. */
> > +	auto itColorSpace = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
> > +					 [&str](const auto &item) {
> > +						 return str == item.second;
> > +					 });
> > +	if (itColorSpace != colorSpaceNames.end())
> > +		return itColorSpace->first;
> > +
> > +	/*
> > +	 * If not found, the string must contain the four color space
> > +	 * components separated by a '/' character.
> > +	 */
> > +	const auto &split = utils::split(str, "/");
> > +	std::vector<std::string> components{ split.begin(), split.end() };
> > +
> > +	if (components.size() != 4)
> > +		return std::nullopt;
> > +
> > +	ColorSpace colorSpace = ColorSpace::Raw;
> > +
> > +	/* Color primaries */
> > +	auto itPrimaries = std::find_if(primariesNames.begin(), primariesNames.end(),
> > +					[&components](const auto &item) {
> > +						return components[0] == item.second;
> > +					});
> > +	if (itPrimaries == primariesNames.end())
> > +		return std::nullopt;
> > +
> > +	colorSpace.primaries = itPrimaries->first;
> > +
> > +	/* Transfer function */
> > +	auto itTransfer = std::find_if(transferNames.begin(), transferNames.end(),
> > +				       [&components](const auto &item) {
> > +					       return components[1] == item.second;
> > +				       });
> > +	if (itTransfer == transferNames.end())
> > +		return std::nullopt;
> > +
> > +	colorSpace.transferFunction = itTransfer->first;
> > +
> > +	/* YCbCr encoding */
> > +	auto itEncoding = std::find_if(encodingNames.begin(), encodingNames.end(),
> > +				       [&components](const auto &item) {
> > +					       return components[2] == item.second;
> > +				       });
> > +	if (itEncoding == encodingNames.end())
> > +		return std::nullopt;
> > +
> > +	colorSpace.ycbcrEncoding = itEncoding->first;
> > +
> > +	/* Quantization range */
> > +	auto itRange = std::find_if(rangeNames.begin(), rangeNames.end(),
> > +				    [&components](const auto &item) {
> > +					    return components[3] == item.second;
> > +				    });
> > +	if (itRange == rangeNames.end())
> > +		return std::nullopt;
> > +
> > +	colorSpace.range = itRange->first;
> > +
> > +	return colorSpace;
> > +}
> > +
> >   /**
> >    * \brief Compare color spaces for equality
> >    * \return True if the two color spaces are identical, false otherwise
Laurent Pinchart Aug. 24, 2022, 9:32 p.m. UTC | #3
On Wed, Aug 24, 2022 at 12:40:59PM +0300, Laurent Pinchart via libcamera-devel wrote:
> On Wed, Aug 24, 2022 at 12:07:59PM +0530, Umang Jain wrote:
> > On 8/23/22 11:13 PM, Laurent Pinchart via libcamera-devel wrote:
> > > Add a ColorSpace:fromString() function to parse a string into a color
> > > space. The string can either contain the name of a well-known color
> > > space, or four color space components separate by a '/' character.
> > 
> > I don't see the order of four components separated by '/' - documented 
> > in the codebase.
> > Should we document the string order as well?
> > 
> > '''
> > The string representation of the colorspace separated by '/' is as follows:
> >      <Color primaries>/<Transfer function>/<Y'CbCr Encoding>/<Range>
> > '''
> 
> I'll expand the documentation of the function to make this more
> explicit.
> 
> I'm also wondering (warning, bikeshedding ahead) if the '/'-separated
> format is the best option. GStreamer uses colons to separate the
> components, and only accepts numerical values for the components in that
> case. I'm not aware of (but haven't looked for) alternatives.

By the way, the '/'-separated format comes from toString(). If we want
to change it, we should update the toString() function as well.

> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > 
> > Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
> > 
> > > ---
> > >   include/libcamera/color_space.h |   2 +
> > >   src/libcamera/color_space.cpp   | 149 +++++++++++++++++++++++++-------
> > >   2 files changed, 121 insertions(+), 30 deletions(-)
> > >
> > > diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h
> > > index 8030a264c66f..f493f72d2db8 100644
> > > --- a/include/libcamera/color_space.h
> > > +++ b/include/libcamera/color_space.h
> > > @@ -59,6 +59,8 @@ public:
> > >   
> > >   	std::string toString() const;
> > >   	static std::string toString(const std::optional<ColorSpace> &colorSpace);
> > > +
> > > +	static std::optional<ColorSpace> fromString(const std::string &str);
> > >   };
> > >   
> > >   bool operator==(const ColorSpace &lhs, const ColorSpace &rhs);
> > > diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp
> > > index 1b2dd2404452..5233626f5ae9 100644
> > > --- a/src/libcamera/color_space.cpp
> > > +++ b/src/libcamera/color_space.cpp
> > > @@ -12,6 +12,9 @@
> > >   #include <map>
> > >   #include <sstream>
> > >   #include <utility>
> > > +#include <vector>
> > > +
> > > +#include <libcamera/base/utils.h>
> > >   
> > >   /**
> > >    * \file color_space.h
> > > @@ -208,6 +211,44 @@ const ColorSpace ColorSpace::Rec2020 = {
> > >    * \brief The pixel range used with by color space
> > >    */
> > >   
> > > +namespace {
> > > +
> > > +const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
> > > +	{ ColorSpace::Raw, "RAW" },
> > > +	{ ColorSpace::Srgb, "sRGB" },
> > > +	{ ColorSpace::Sycc, "sYCC" },
> > > +	{ ColorSpace::Smpte170m, "SMPTE170M" },
> > > +	{ ColorSpace::Rec709, "Rec709" },
> > > +	{ ColorSpace::Rec2020, "Rec2020" },
> > > +} };
> > > +
> > > +const std::map<ColorSpace::Primaries, std::string> primariesNames = {
> > > +	{ ColorSpace::Primaries::Raw, "RAW" },
> > > +	{ ColorSpace::Primaries::Smpte170m, "SMPTE170M" },
> > > +	{ ColorSpace::Primaries::Rec709, "Rec709" },
> > > +	{ ColorSpace::Primaries::Rec2020, "Rec2020" },
> > > +};
> > > +
> > > +const std::map<ColorSpace::TransferFunction, std::string> transferNames = {
> > > +	{ ColorSpace::TransferFunction::Linear, "Linear" },
> > > +	{ ColorSpace::TransferFunction::Srgb, "sRGB" },
> > > +	{ ColorSpace::TransferFunction::Rec709, "Rec709" },
> > > +};
> > > +
> > > +const std::map<ColorSpace::YcbcrEncoding, std::string> encodingNames = {
> > > +	{ ColorSpace::YcbcrEncoding::None, "None" },
> > > +	{ ColorSpace::YcbcrEncoding::Rec601, "Rec601" },
> > > +	{ ColorSpace::YcbcrEncoding::Rec709, "Rec709" },
> > > +	{ ColorSpace::YcbcrEncoding::Rec2020, "Rec2020" },
> > > +};
> > > +
> > > +const std::map<ColorSpace::Range, std::string> rangeNames = {
> > > +	{ ColorSpace::Range::Full, "Full" },
> > > +	{ ColorSpace::Range::Limited, "Limited" },
> > > +};
> > > +
> > > +} /* namespace */
> > > +
> > >   /**
> > >    * \brief Assemble and return a readable string representation of the
> > >    * ColorSpace
> > > @@ -223,14 +264,6 @@ std::string ColorSpace::toString() const
> > >   {
> > >   	/* Print out a brief name only for standard color spaces. */
> > >   
> > > -	static const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
> > > -		{ ColorSpace::Raw, "RAW" },
> > > -		{ ColorSpace::Srgb, "sRGB" },
> > > -		{ ColorSpace::Sycc, "sYCC" },
> > > -		{ ColorSpace::Smpte170m, "SMPTE170M" },
> > > -		{ ColorSpace::Rec709, "Rec709" },
> > > -		{ ColorSpace::Rec2020, "Rec2020" },
> > > -	} };
> > >   	auto it = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
> > >   			       [this](const auto &item) {
> > >   				       return *this == item.first;
> > > @@ -240,28 +273,6 @@ std::string ColorSpace::toString() const
> > >   
> > >   	/* Assemble a name made of the constituent fields. */
> > >   
> > > -	static const std::map<Primaries, std::string> primariesNames = {
> > > -		{ Primaries::Raw, "RAW" },
> > > -		{ Primaries::Smpte170m, "SMPTE170M" },
> > > -		{ Primaries::Rec709, "Rec709" },
> > > -		{ Primaries::Rec2020, "Rec2020" },
> > > -	};
> > > -	static const std::map<TransferFunction, std::string> transferNames = {
> > > -		{ TransferFunction::Linear, "Linear" },
> > > -		{ TransferFunction::Srgb, "sRGB" },
> > > -		{ TransferFunction::Rec709, "Rec709" },
> > > -	};
> > > -	static const std::map<YcbcrEncoding, std::string> encodingNames = {
> > > -		{ YcbcrEncoding::None, "None" },
> > > -		{ YcbcrEncoding::Rec601, "Rec601" },
> > > -		{ YcbcrEncoding::Rec709, "Rec709" },
> > > -		{ YcbcrEncoding::Rec2020, "Rec2020" },
> > > -	};
> > > -	static const std::map<Range, std::string> rangeNames = {
> > > -		{ Range::Full, "Full" },
> > > -		{ Range::Limited, "Limited" },
> > > -	};
> > > -
> > >   	auto itPrimaries = primariesNames.find(primaries);
> > >   	std::string primariesName =
> > >   		itPrimaries == primariesNames.end() ? "Invalid" : itPrimaries->second;
> > > @@ -303,6 +314,84 @@ std::string ColorSpace::toString(const std::optional<ColorSpace> &colorSpace)
> > >   	return colorSpace->toString();
> > >   }
> > >   
> > > +/**
> > > + * \brief Construct a color space from a string
> > > + * \param[in] str The string
> > > + *
> > > + * The string \a str can contain the name of a well-known color space, or be
> > > + * made of the four color space components separate by a '/' character. Any
> > > + * failure to parse the string, either because it doesn't match the expected
> > > + * format, or because the one of the names isn't recognized, will cause this
> > > + * function to return std::nullopt.
> > > + *
> > > + * \return The ColorSpace corresponding to the string, or std::nullopt if the
> > > + * string doesn't describe a known color space
> > > + */
> > > +std::optional<ColorSpace> ColorSpace::fromString(const std::string &str)
> > > +{
> > > +	/* First search for a standard color space name match. */
> > > +	auto itColorSpace = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
> > > +					 [&str](const auto &item) {
> > > +						 return str == item.second;
> > > +					 });
> > > +	if (itColorSpace != colorSpaceNames.end())
> > > +		return itColorSpace->first;
> > > +
> > > +	/*
> > > +	 * If not found, the string must contain the four color space
> > > +	 * components separated by a '/' character.
> > > +	 */
> > > +	const auto &split = utils::split(str, "/");
> > > +	std::vector<std::string> components{ split.begin(), split.end() };
> > > +
> > > +	if (components.size() != 4)
> > > +		return std::nullopt;
> > > +
> > > +	ColorSpace colorSpace = ColorSpace::Raw;
> > > +
> > > +	/* Color primaries */
> > > +	auto itPrimaries = std::find_if(primariesNames.begin(), primariesNames.end(),
> > > +					[&components](const auto &item) {
> > > +						return components[0] == item.second;
> > > +					});
> > > +	if (itPrimaries == primariesNames.end())
> > > +		return std::nullopt;
> > > +
> > > +	colorSpace.primaries = itPrimaries->first;
> > > +
> > > +	/* Transfer function */
> > > +	auto itTransfer = std::find_if(transferNames.begin(), transferNames.end(),
> > > +				       [&components](const auto &item) {
> > > +					       return components[1] == item.second;
> > > +				       });
> > > +	if (itTransfer == transferNames.end())
> > > +		return std::nullopt;
> > > +
> > > +	colorSpace.transferFunction = itTransfer->first;
> > > +
> > > +	/* YCbCr encoding */
> > > +	auto itEncoding = std::find_if(encodingNames.begin(), encodingNames.end(),
> > > +				       [&components](const auto &item) {
> > > +					       return components[2] == item.second;
> > > +				       });
> > > +	if (itEncoding == encodingNames.end())
> > > +		return std::nullopt;
> > > +
> > > +	colorSpace.ycbcrEncoding = itEncoding->first;
> > > +
> > > +	/* Quantization range */
> > > +	auto itRange = std::find_if(rangeNames.begin(), rangeNames.end(),
> > > +				    [&components](const auto &item) {
> > > +					    return components[3] == item.second;
> > > +				    });
> > > +	if (itRange == rangeNames.end())
> > > +		return std::nullopt;
> > > +
> > > +	colorSpace.range = itRange->first;
> > > +
> > > +	return colorSpace;
> > > +}
> > > +
> > >   /**
> > >    * \brief Compare color spaces for equality
> > >    * \return True if the two color spaces are identical, false otherwise
Umang Jain Aug. 25, 2022, 1:19 p.m. UTC | #4
Hi Laurent,

On 8/25/22 3:02 AM, Laurent Pinchart wrote:
> On Wed, Aug 24, 2022 at 12:40:59PM +0300, Laurent Pinchart via libcamera-devel wrote:
>> On Wed, Aug 24, 2022 at 12:07:59PM +0530, Umang Jain wrote:
>>> On 8/23/22 11:13 PM, Laurent Pinchart via libcamera-devel wrote:
>>>> Add a ColorSpace:fromString() function to parse a string into a color
>>>> space. The string can either contain the name of a well-known color
>>>> space, or four color space components separate by a '/' character.
>>> I don't see the order of four components separated by '/' - documented
>>> in the codebase.
>>> Should we document the string order as well?
>>>
>>> '''
>>> The string representation of the colorspace separated by '/' is as follows:
>>>       <Color primaries>/<Transfer function>/<Y'CbCr Encoding>/<Range>
>>> '''
>> I'll expand the documentation of the function to make this more
>> explicit.

Thank you!
>>
>> I'm also wondering (warning, bikeshedding ahead) if the '/'-separated
>> format is the best option. GStreamer uses colons to separate the
>> components, and only accepts numerical values for the components in that
>> case. I'm not aware of (but haven't looked for) alternatives.

Oh, so we can't really align with gstreamer using the colon(':') because 
it would split awkwardly with utils::split(str, ":")  I think

ColorSpace::Primaries::Rec709:ColorSpace::TransferFunction::Rec709:ColorSpace::YcbcrEncoding::Rec709:ColorSpace::Range::Full

In that case, I think "/" is our best choice. Second best I think will 
be "|"

I saw a few Colorspace::toString() randomly on the internet, but none 
were structural. They were mostly just meant to print it out for logs 
(and not treating as string input).

I also saw DRM also has colorspaces but haven't came across a helper 
like we need. I am not sure if it's worth to look further! "/" is 
already a good starting point, given our requirements.
> By the way, the '/'-separated format comes from toString(). If we want
> to change it, we should update the toString() function as well.

Ack.
>
>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
>>>
>>>> ---
>>>>    include/libcamera/color_space.h |   2 +
>>>>    src/libcamera/color_space.cpp   | 149 +++++++++++++++++++++++++-------
>>>>    2 files changed, 121 insertions(+), 30 deletions(-)
>>>>
>>>> diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h
>>>> index 8030a264c66f..f493f72d2db8 100644
>>>> --- a/include/libcamera/color_space.h
>>>> +++ b/include/libcamera/color_space.h
>>>> @@ -59,6 +59,8 @@ public:
>>>>    
>>>>    	std::string toString() const;
>>>>    	static std::string toString(const std::optional<ColorSpace> &colorSpace);
>>>> +
>>>> +	static std::optional<ColorSpace> fromString(const std::string &str);
>>>>    };
>>>>    
>>>>    bool operator==(const ColorSpace &lhs, const ColorSpace &rhs);
>>>> diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp
>>>> index 1b2dd2404452..5233626f5ae9 100644
>>>> --- a/src/libcamera/color_space.cpp
>>>> +++ b/src/libcamera/color_space.cpp
>>>> @@ -12,6 +12,9 @@
>>>>    #include <map>
>>>>    #include <sstream>
>>>>    #include <utility>
>>>> +#include <vector>
>>>> +
>>>> +#include <libcamera/base/utils.h>
>>>>    
>>>>    /**
>>>>     * \file color_space.h
>>>> @@ -208,6 +211,44 @@ const ColorSpace ColorSpace::Rec2020 = {
>>>>     * \brief The pixel range used with by color space
>>>>     */
>>>>    
>>>> +namespace {
>>>> +
>>>> +const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
>>>> +	{ ColorSpace::Raw, "RAW" },
>>>> +	{ ColorSpace::Srgb, "sRGB" },
>>>> +	{ ColorSpace::Sycc, "sYCC" },
>>>> +	{ ColorSpace::Smpte170m, "SMPTE170M" },
>>>> +	{ ColorSpace::Rec709, "Rec709" },
>>>> +	{ ColorSpace::Rec2020, "Rec2020" },
>>>> +} };
>>>> +
>>>> +const std::map<ColorSpace::Primaries, std::string> primariesNames = {
>>>> +	{ ColorSpace::Primaries::Raw, "RAW" },
>>>> +	{ ColorSpace::Primaries::Smpte170m, "SMPTE170M" },
>>>> +	{ ColorSpace::Primaries::Rec709, "Rec709" },
>>>> +	{ ColorSpace::Primaries::Rec2020, "Rec2020" },
>>>> +};
>>>> +
>>>> +const std::map<ColorSpace::TransferFunction, std::string> transferNames = {
>>>> +	{ ColorSpace::TransferFunction::Linear, "Linear" },
>>>> +	{ ColorSpace::TransferFunction::Srgb, "sRGB" },
>>>> +	{ ColorSpace::TransferFunction::Rec709, "Rec709" },
>>>> +};
>>>> +
>>>> +const std::map<ColorSpace::YcbcrEncoding, std::string> encodingNames = {
>>>> +	{ ColorSpace::YcbcrEncoding::None, "None" },
>>>> +	{ ColorSpace::YcbcrEncoding::Rec601, "Rec601" },
>>>> +	{ ColorSpace::YcbcrEncoding::Rec709, "Rec709" },
>>>> +	{ ColorSpace::YcbcrEncoding::Rec2020, "Rec2020" },
>>>> +};
>>>> +
>>>> +const std::map<ColorSpace::Range, std::string> rangeNames = {
>>>> +	{ ColorSpace::Range::Full, "Full" },
>>>> +	{ ColorSpace::Range::Limited, "Limited" },
>>>> +};
>>>> +
>>>> +} /* namespace */
>>>> +
>>>>    /**
>>>>     * \brief Assemble and return a readable string representation of the
>>>>     * ColorSpace
>>>> @@ -223,14 +264,6 @@ std::string ColorSpace::toString() const
>>>>    {
>>>>    	/* Print out a brief name only for standard color spaces. */
>>>>    
>>>> -	static const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
>>>> -		{ ColorSpace::Raw, "RAW" },
>>>> -		{ ColorSpace::Srgb, "sRGB" },
>>>> -		{ ColorSpace::Sycc, "sYCC" },
>>>> -		{ ColorSpace::Smpte170m, "SMPTE170M" },
>>>> -		{ ColorSpace::Rec709, "Rec709" },
>>>> -		{ ColorSpace::Rec2020, "Rec2020" },
>>>> -	} };
>>>>    	auto it = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
>>>>    			       [this](const auto &item) {
>>>>    				       return *this == item.first;
>>>> @@ -240,28 +273,6 @@ std::string ColorSpace::toString() const
>>>>    
>>>>    	/* Assemble a name made of the constituent fields. */
>>>>    
>>>> -	static const std::map<Primaries, std::string> primariesNames = {
>>>> -		{ Primaries::Raw, "RAW" },
>>>> -		{ Primaries::Smpte170m, "SMPTE170M" },
>>>> -		{ Primaries::Rec709, "Rec709" },
>>>> -		{ Primaries::Rec2020, "Rec2020" },
>>>> -	};
>>>> -	static const std::map<TransferFunction, std::string> transferNames = {
>>>> -		{ TransferFunction::Linear, "Linear" },
>>>> -		{ TransferFunction::Srgb, "sRGB" },
>>>> -		{ TransferFunction::Rec709, "Rec709" },
>>>> -	};
>>>> -	static const std::map<YcbcrEncoding, std::string> encodingNames = {
>>>> -		{ YcbcrEncoding::None, "None" },
>>>> -		{ YcbcrEncoding::Rec601, "Rec601" },
>>>> -		{ YcbcrEncoding::Rec709, "Rec709" },
>>>> -		{ YcbcrEncoding::Rec2020, "Rec2020" },
>>>> -	};
>>>> -	static const std::map<Range, std::string> rangeNames = {
>>>> -		{ Range::Full, "Full" },
>>>> -		{ Range::Limited, "Limited" },
>>>> -	};
>>>> -
>>>>    	auto itPrimaries = primariesNames.find(primaries);
>>>>    	std::string primariesName =
>>>>    		itPrimaries == primariesNames.end() ? "Invalid" : itPrimaries->second;
>>>> @@ -303,6 +314,84 @@ std::string ColorSpace::toString(const std::optional<ColorSpace> &colorSpace)
>>>>    	return colorSpace->toString();
>>>>    }
>>>>    
>>>> +/**
>>>> + * \brief Construct a color space from a string
>>>> + * \param[in] str The string
>>>> + *
>>>> + * The string \a str can contain the name of a well-known color space, or be
>>>> + * made of the four color space components separate by a '/' character. Any
>>>> + * failure to parse the string, either because it doesn't match the expected
>>>> + * format, or because the one of the names isn't recognized, will cause this
>>>> + * function to return std::nullopt.
>>>> + *
>>>> + * \return The ColorSpace corresponding to the string, or std::nullopt if the
>>>> + * string doesn't describe a known color space
>>>> + */
>>>> +std::optional<ColorSpace> ColorSpace::fromString(const std::string &str)
>>>> +{
>>>> +	/* First search for a standard color space name match. */
>>>> +	auto itColorSpace = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
>>>> +					 [&str](const auto &item) {
>>>> +						 return str == item.second;
>>>> +					 });
>>>> +	if (itColorSpace != colorSpaceNames.end())
>>>> +		return itColorSpace->first;
>>>> +
>>>> +	/*
>>>> +	 * If not found, the string must contain the four color space
>>>> +	 * components separated by a '/' character.
>>>> +	 */
>>>> +	const auto &split = utils::split(str, "/");
>>>> +	std::vector<std::string> components{ split.begin(), split.end() };
>>>> +
>>>> +	if (components.size() != 4)
>>>> +		return std::nullopt;
>>>> +
>>>> +	ColorSpace colorSpace = ColorSpace::Raw;
>>>> +
>>>> +	/* Color primaries */
>>>> +	auto itPrimaries = std::find_if(primariesNames.begin(), primariesNames.end(),
>>>> +					[&components](const auto &item) {
>>>> +						return components[0] == item.second;
>>>> +					});
>>>> +	if (itPrimaries == primariesNames.end())
>>>> +		return std::nullopt;
>>>> +
>>>> +	colorSpace.primaries = itPrimaries->first;
>>>> +
>>>> +	/* Transfer function */
>>>> +	auto itTransfer = std::find_if(transferNames.begin(), transferNames.end(),
>>>> +				       [&components](const auto &item) {
>>>> +					       return components[1] == item.second;
>>>> +				       });
>>>> +	if (itTransfer == transferNames.end())
>>>> +		return std::nullopt;
>>>> +
>>>> +	colorSpace.transferFunction = itTransfer->first;
>>>> +
>>>> +	/* YCbCr encoding */
>>>> +	auto itEncoding = std::find_if(encodingNames.begin(), encodingNames.end(),
>>>> +				       [&components](const auto &item) {
>>>> +					       return components[2] == item.second;
>>>> +				       });
>>>> +	if (itEncoding == encodingNames.end())
>>>> +		return std::nullopt;
>>>> +
>>>> +	colorSpace.ycbcrEncoding = itEncoding->first;
>>>> +
>>>> +	/* Quantization range */
>>>> +	auto itRange = std::find_if(rangeNames.begin(), rangeNames.end(),
>>>> +				    [&components](const auto &item) {
>>>> +					    return components[3] == item.second;
>>>> +				    });
>>>> +	if (itRange == rangeNames.end())
>>>> +		return std::nullopt;
>>>> +
>>>> +	colorSpace.range = itRange->first;
>>>> +
>>>> +	return colorSpace;
>>>> +}
>>>> +
>>>>    /**
>>>>     * \brief Compare color spaces for equality
>>>>     * \return True if the two color spaces are identical, false otherwise
Laurent Pinchart Aug. 25, 2022, 2:36 p.m. UTC | #5
Hi Umang,

On Thu, Aug 25, 2022 at 06:49:53PM +0530, Umang Jain wrote:
> On 8/25/22 3:02 AM, Laurent Pinchart wrote:
> > On Wed, Aug 24, 2022 at 12:40:59PM +0300, Laurent Pinchart via libcamera-devel wrote:
> >> On Wed, Aug 24, 2022 at 12:07:59PM +0530, Umang Jain wrote:
> >>> On 8/23/22 11:13 PM, Laurent Pinchart via libcamera-devel wrote:
> >>>> Add a ColorSpace:fromString() function to parse a string into a color
> >>>> space. The string can either contain the name of a well-known color
> >>>> space, or four color space components separate by a '/' character.
> >>> 
> >>> I don't see the order of four components separated by '/' - documented
> >>> in the codebase.
> >>> Should we document the string order as well?
> >>>
> >>> '''
> >>> The string representation of the colorspace separated by '/' is as follows:
> >>>       <Color primaries>/<Transfer function>/<Y'CbCr Encoding>/<Range>
> >>> '''
> >> 
> >> I'll expand the documentation of the function to make this more
> >> explicit.
> 
> Thank you!
> 
> >> I'm also wondering (warning, bikeshedding ahead) if the '/'-separated
> >> format is the best option. GStreamer uses colons to separate the
> >> components, and only accepts numerical values for the components in that
> >> case. I'm not aware of (but haven't looked for) alternatives.
> 
> Oh, so we can't really align with gstreamer using the colon(':') because 
> it would split awkwardly with utils::split(str, ":")  I think
> 
> ColorSpace::Primaries::Rec709:ColorSpace::TransferFunction::Rec709:ColorSpace::YcbcrEncoding::Rec709:ColorSpace::Range::Full

The string doesn't have to fully qualify the components, it would be

	Rec709:Rec709:Rec709:Full

> In that case, I think "/" is our best choice. Second best I think will 
> be "|"
> 
> I saw a few Colorspace::toString() randomly on the internet, but none 
> were structural. They were mostly just meant to print it out for logs 
> (and not treating as string input).
> 
> I also saw DRM also has colorspaces but haven't came across a helper 
> like we need. I am not sure if it's worth to look further! "/" is 
> already a good starting point, given our requirements.
> 
> > By the way, the '/'-separated format comes from toString(). If we want
> > to change it, we should update the toString() function as well.
> 
> Ack.
> 
> >>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >>> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
> >>>
> >>>> ---
> >>>>    include/libcamera/color_space.h |   2 +
> >>>>    src/libcamera/color_space.cpp   | 149 +++++++++++++++++++++++++-------
> >>>>    2 files changed, 121 insertions(+), 30 deletions(-)
> >>>>
> >>>> diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h
> >>>> index 8030a264c66f..f493f72d2db8 100644
> >>>> --- a/include/libcamera/color_space.h
> >>>> +++ b/include/libcamera/color_space.h
> >>>> @@ -59,6 +59,8 @@ public:
> >>>>    
> >>>>    	std::string toString() const;
> >>>>    	static std::string toString(const std::optional<ColorSpace> &colorSpace);
> >>>> +
> >>>> +	static std::optional<ColorSpace> fromString(const std::string &str);
> >>>>    };
> >>>>    
> >>>>    bool operator==(const ColorSpace &lhs, const ColorSpace &rhs);
> >>>> diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp
> >>>> index 1b2dd2404452..5233626f5ae9 100644
> >>>> --- a/src/libcamera/color_space.cpp
> >>>> +++ b/src/libcamera/color_space.cpp
> >>>> @@ -12,6 +12,9 @@
> >>>>    #include <map>
> >>>>    #include <sstream>
> >>>>    #include <utility>
> >>>> +#include <vector>
> >>>> +
> >>>> +#include <libcamera/base/utils.h>
> >>>>    
> >>>>    /**
> >>>>     * \file color_space.h
> >>>> @@ -208,6 +211,44 @@ const ColorSpace ColorSpace::Rec2020 = {
> >>>>     * \brief The pixel range used with by color space
> >>>>     */
> >>>>    
> >>>> +namespace {
> >>>> +
> >>>> +const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
> >>>> +	{ ColorSpace::Raw, "RAW" },
> >>>> +	{ ColorSpace::Srgb, "sRGB" },
> >>>> +	{ ColorSpace::Sycc, "sYCC" },
> >>>> +	{ ColorSpace::Smpte170m, "SMPTE170M" },
> >>>> +	{ ColorSpace::Rec709, "Rec709" },
> >>>> +	{ ColorSpace::Rec2020, "Rec2020" },
> >>>> +} };
> >>>> +
> >>>> +const std::map<ColorSpace::Primaries, std::string> primariesNames = {
> >>>> +	{ ColorSpace::Primaries::Raw, "RAW" },
> >>>> +	{ ColorSpace::Primaries::Smpte170m, "SMPTE170M" },
> >>>> +	{ ColorSpace::Primaries::Rec709, "Rec709" },
> >>>> +	{ ColorSpace::Primaries::Rec2020, "Rec2020" },
> >>>> +};
> >>>> +
> >>>> +const std::map<ColorSpace::TransferFunction, std::string> transferNames = {
> >>>> +	{ ColorSpace::TransferFunction::Linear, "Linear" },
> >>>> +	{ ColorSpace::TransferFunction::Srgb, "sRGB" },
> >>>> +	{ ColorSpace::TransferFunction::Rec709, "Rec709" },
> >>>> +};
> >>>> +
> >>>> +const std::map<ColorSpace::YcbcrEncoding, std::string> encodingNames = {
> >>>> +	{ ColorSpace::YcbcrEncoding::None, "None" },
> >>>> +	{ ColorSpace::YcbcrEncoding::Rec601, "Rec601" },
> >>>> +	{ ColorSpace::YcbcrEncoding::Rec709, "Rec709" },
> >>>> +	{ ColorSpace::YcbcrEncoding::Rec2020, "Rec2020" },
> >>>> +};
> >>>> +
> >>>> +const std::map<ColorSpace::Range, std::string> rangeNames = {
> >>>> +	{ ColorSpace::Range::Full, "Full" },
> >>>> +	{ ColorSpace::Range::Limited, "Limited" },
> >>>> +};
> >>>> +
> >>>> +} /* namespace */
> >>>> +
> >>>>    /**
> >>>>     * \brief Assemble and return a readable string representation of the
> >>>>     * ColorSpace
> >>>> @@ -223,14 +264,6 @@ std::string ColorSpace::toString() const
> >>>>    {
> >>>>    	/* Print out a brief name only for standard color spaces. */
> >>>>    
> >>>> -	static const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
> >>>> -		{ ColorSpace::Raw, "RAW" },
> >>>> -		{ ColorSpace::Srgb, "sRGB" },
> >>>> -		{ ColorSpace::Sycc, "sYCC" },
> >>>> -		{ ColorSpace::Smpte170m, "SMPTE170M" },
> >>>> -		{ ColorSpace::Rec709, "Rec709" },
> >>>> -		{ ColorSpace::Rec2020, "Rec2020" },
> >>>> -	} };
> >>>>    	auto it = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
> >>>>    			       [this](const auto &item) {
> >>>>    				       return *this == item.first;
> >>>> @@ -240,28 +273,6 @@ std::string ColorSpace::toString() const
> >>>>    
> >>>>    	/* Assemble a name made of the constituent fields. */
> >>>>    
> >>>> -	static const std::map<Primaries, std::string> primariesNames = {
> >>>> -		{ Primaries::Raw, "RAW" },
> >>>> -		{ Primaries::Smpte170m, "SMPTE170M" },
> >>>> -		{ Primaries::Rec709, "Rec709" },
> >>>> -		{ Primaries::Rec2020, "Rec2020" },
> >>>> -	};
> >>>> -	static const std::map<TransferFunction, std::string> transferNames = {
> >>>> -		{ TransferFunction::Linear, "Linear" },
> >>>> -		{ TransferFunction::Srgb, "sRGB" },
> >>>> -		{ TransferFunction::Rec709, "Rec709" },
> >>>> -	};
> >>>> -	static const std::map<YcbcrEncoding, std::string> encodingNames = {
> >>>> -		{ YcbcrEncoding::None, "None" },
> >>>> -		{ YcbcrEncoding::Rec601, "Rec601" },
> >>>> -		{ YcbcrEncoding::Rec709, "Rec709" },
> >>>> -		{ YcbcrEncoding::Rec2020, "Rec2020" },
> >>>> -	};
> >>>> -	static const std::map<Range, std::string> rangeNames = {
> >>>> -		{ Range::Full, "Full" },
> >>>> -		{ Range::Limited, "Limited" },
> >>>> -	};
> >>>> -
> >>>>    	auto itPrimaries = primariesNames.find(primaries);
> >>>>    	std::string primariesName =
> >>>>    		itPrimaries == primariesNames.end() ? "Invalid" : itPrimaries->second;
> >>>> @@ -303,6 +314,84 @@ std::string ColorSpace::toString(const std::optional<ColorSpace> &colorSpace)
> >>>>    	return colorSpace->toString();
> >>>>    }
> >>>>    
> >>>> +/**
> >>>> + * \brief Construct a color space from a string
> >>>> + * \param[in] str The string
> >>>> + *
> >>>> + * The string \a str can contain the name of a well-known color space, or be
> >>>> + * made of the four color space components separate by a '/' character. Any
> >>>> + * failure to parse the string, either because it doesn't match the expected
> >>>> + * format, or because the one of the names isn't recognized, will cause this
> >>>> + * function to return std::nullopt.
> >>>> + *
> >>>> + * \return The ColorSpace corresponding to the string, or std::nullopt if the
> >>>> + * string doesn't describe a known color space
> >>>> + */
> >>>> +std::optional<ColorSpace> ColorSpace::fromString(const std::string &str)
> >>>> +{
> >>>> +	/* First search for a standard color space name match. */
> >>>> +	auto itColorSpace = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
> >>>> +					 [&str](const auto &item) {
> >>>> +						 return str == item.second;
> >>>> +					 });
> >>>> +	if (itColorSpace != colorSpaceNames.end())
> >>>> +		return itColorSpace->first;
> >>>> +
> >>>> +	/*
> >>>> +	 * If not found, the string must contain the four color space
> >>>> +	 * components separated by a '/' character.
> >>>> +	 */
> >>>> +	const auto &split = utils::split(str, "/");
> >>>> +	std::vector<std::string> components{ split.begin(), split.end() };
> >>>> +
> >>>> +	if (components.size() != 4)
> >>>> +		return std::nullopt;
> >>>> +
> >>>> +	ColorSpace colorSpace = ColorSpace::Raw;
> >>>> +
> >>>> +	/* Color primaries */
> >>>> +	auto itPrimaries = std::find_if(primariesNames.begin(), primariesNames.end(),
> >>>> +					[&components](const auto &item) {
> >>>> +						return components[0] == item.second;
> >>>> +					});
> >>>> +	if (itPrimaries == primariesNames.end())
> >>>> +		return std::nullopt;
> >>>> +
> >>>> +	colorSpace.primaries = itPrimaries->first;
> >>>> +
> >>>> +	/* Transfer function */
> >>>> +	auto itTransfer = std::find_if(transferNames.begin(), transferNames.end(),
> >>>> +				       [&components](const auto &item) {
> >>>> +					       return components[1] == item.second;
> >>>> +				       });
> >>>> +	if (itTransfer == transferNames.end())
> >>>> +		return std::nullopt;
> >>>> +
> >>>> +	colorSpace.transferFunction = itTransfer->first;
> >>>> +
> >>>> +	/* YCbCr encoding */
> >>>> +	auto itEncoding = std::find_if(encodingNames.begin(), encodingNames.end(),
> >>>> +				       [&components](const auto &item) {
> >>>> +					       return components[2] == item.second;
> >>>> +				       });
> >>>> +	if (itEncoding == encodingNames.end())
> >>>> +		return std::nullopt;
> >>>> +
> >>>> +	colorSpace.ycbcrEncoding = itEncoding->first;
> >>>> +
> >>>> +	/* Quantization range */
> >>>> +	auto itRange = std::find_if(rangeNames.begin(), rangeNames.end(),
> >>>> +				    [&components](const auto &item) {
> >>>> +					    return components[3] == item.second;
> >>>> +				    });
> >>>> +	if (itRange == rangeNames.end())
> >>>> +		return std::nullopt;
> >>>> +
> >>>> +	colorSpace.range = itRange->first;
> >>>> +
> >>>> +	return colorSpace;
> >>>> +}
> >>>> +
> >>>>    /**
> >>>>     * \brief Compare color spaces for equality
> >>>>     * \return True if the two color spaces are identical, false otherwise
Umang Jain Aug. 25, 2022, 3:43 p.m. UTC | #6
On 8/25/22 8:06 PM, Laurent Pinchart wrote:
> Hi Umang,
>
> On Thu, Aug 25, 2022 at 06:49:53PM +0530, Umang Jain wrote:
>> On 8/25/22 3:02 AM, Laurent Pinchart wrote:
>>> On Wed, Aug 24, 2022 at 12:40:59PM +0300, Laurent Pinchart via libcamera-devel wrote:
>>>> On Wed, Aug 24, 2022 at 12:07:59PM +0530, Umang Jain wrote:
>>>>> On 8/23/22 11:13 PM, Laurent Pinchart via libcamera-devel wrote:
>>>>>> Add a ColorSpace:fromString() function to parse a string into a color
>>>>>> space. The string can either contain the name of a well-known color
>>>>>> space, or four color space components separate by a '/' character.
>>>>> I don't see the order of four components separated by '/' - documented
>>>>> in the codebase.
>>>>> Should we document the string order as well?
>>>>>
>>>>> '''
>>>>> The string representation of the colorspace separated by '/' is as follows:
>>>>>        <Color primaries>/<Transfer function>/<Y'CbCr Encoding>/<Range>
>>>>> '''
>>>> I'll expand the documentation of the function to make this more
>>>> explicit.
>> Thank you!
>>
>>>> I'm also wondering (warning, bikeshedding ahead) if the '/'-separated
>>>> format is the best option. GStreamer uses colons to separate the
>>>> components, and only accepts numerical values for the components in that
>>>> case. I'm not aware of (but haven't looked for) alternatives.
>> Oh, so we can't really align with gstreamer using the colon(':') because
>> it would split awkwardly with utils::split(str, ":")  I think
>>
>> ColorSpace::Primaries::Rec709:ColorSpace::TransferFunction::Rec709:ColorSpace::YcbcrEncoding::Rec709:ColorSpace::Range::Full
> The string doesn't have to fully qualify the components, it would be
>
> 	Rec709:Rec709:Rec709:Full

Yes correct, it can be - Can we keep ":" as the splitter then?  ;-)
>
>> In that case, I think "/" is our best choice. Second best I think will
>> be "|"
>>
>> I saw a few Colorspace::toString() randomly on the internet, but none
>> were structural. They were mostly just meant to print it out for logs
>> (and not treating as string input).
>>
>> I also saw DRM also has colorspaces but haven't came across a helper
>> like we need. I am not sure if it's worth to look further! "/" is
>> already a good starting point, given our requirements.
>>
>>> By the way, the '/'-separated format comes from toString(). If we want
>>> to change it, we should update the toString() function as well.
>> Ack.
>>
>>>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>>>> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
>>>>>
>>>>>> ---
>>>>>>     include/libcamera/color_space.h |   2 +
>>>>>>     src/libcamera/color_space.cpp   | 149 +++++++++++++++++++++++++-------
>>>>>>     2 files changed, 121 insertions(+), 30 deletions(-)
>>>>>>
>>>>>> diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h
>>>>>> index 8030a264c66f..f493f72d2db8 100644
>>>>>> --- a/include/libcamera/color_space.h
>>>>>> +++ b/include/libcamera/color_space.h
>>>>>> @@ -59,6 +59,8 @@ public:
>>>>>>     
>>>>>>     	std::string toString() const;
>>>>>>     	static std::string toString(const std::optional<ColorSpace> &colorSpace);
>>>>>> +
>>>>>> +	static std::optional<ColorSpace> fromString(const std::string &str);
>>>>>>     };
>>>>>>     
>>>>>>     bool operator==(const ColorSpace &lhs, const ColorSpace &rhs);
>>>>>> diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp
>>>>>> index 1b2dd2404452..5233626f5ae9 100644
>>>>>> --- a/src/libcamera/color_space.cpp
>>>>>> +++ b/src/libcamera/color_space.cpp
>>>>>> @@ -12,6 +12,9 @@
>>>>>>     #include <map>
>>>>>>     #include <sstream>
>>>>>>     #include <utility>
>>>>>> +#include <vector>
>>>>>> +
>>>>>> +#include <libcamera/base/utils.h>
>>>>>>     
>>>>>>     /**
>>>>>>      * \file color_space.h
>>>>>> @@ -208,6 +211,44 @@ const ColorSpace ColorSpace::Rec2020 = {
>>>>>>      * \brief The pixel range used with by color space
>>>>>>      */
>>>>>>     
>>>>>> +namespace {
>>>>>> +
>>>>>> +const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
>>>>>> +	{ ColorSpace::Raw, "RAW" },
>>>>>> +	{ ColorSpace::Srgb, "sRGB" },
>>>>>> +	{ ColorSpace::Sycc, "sYCC" },
>>>>>> +	{ ColorSpace::Smpte170m, "SMPTE170M" },
>>>>>> +	{ ColorSpace::Rec709, "Rec709" },
>>>>>> +	{ ColorSpace::Rec2020, "Rec2020" },
>>>>>> +} };
>>>>>> +
>>>>>> +const std::map<ColorSpace::Primaries, std::string> primariesNames = {
>>>>>> +	{ ColorSpace::Primaries::Raw, "RAW" },
>>>>>> +	{ ColorSpace::Primaries::Smpte170m, "SMPTE170M" },
>>>>>> +	{ ColorSpace::Primaries::Rec709, "Rec709" },
>>>>>> +	{ ColorSpace::Primaries::Rec2020, "Rec2020" },
>>>>>> +};
>>>>>> +
>>>>>> +const std::map<ColorSpace::TransferFunction, std::string> transferNames = {
>>>>>> +	{ ColorSpace::TransferFunction::Linear, "Linear" },
>>>>>> +	{ ColorSpace::TransferFunction::Srgb, "sRGB" },
>>>>>> +	{ ColorSpace::TransferFunction::Rec709, "Rec709" },
>>>>>> +};
>>>>>> +
>>>>>> +const std::map<ColorSpace::YcbcrEncoding, std::string> encodingNames = {
>>>>>> +	{ ColorSpace::YcbcrEncoding::None, "None" },
>>>>>> +	{ ColorSpace::YcbcrEncoding::Rec601, "Rec601" },
>>>>>> +	{ ColorSpace::YcbcrEncoding::Rec709, "Rec709" },
>>>>>> +	{ ColorSpace::YcbcrEncoding::Rec2020, "Rec2020" },
>>>>>> +};
>>>>>> +
>>>>>> +const std::map<ColorSpace::Range, std::string> rangeNames = {
>>>>>> +	{ ColorSpace::Range::Full, "Full" },
>>>>>> +	{ ColorSpace::Range::Limited, "Limited" },
>>>>>> +};
>>>>>> +
>>>>>> +} /* namespace */
>>>>>> +
>>>>>>     /**
>>>>>>      * \brief Assemble and return a readable string representation of the
>>>>>>      * ColorSpace
>>>>>> @@ -223,14 +264,6 @@ std::string ColorSpace::toString() const
>>>>>>     {
>>>>>>     	/* Print out a brief name only for standard color spaces. */
>>>>>>     
>>>>>> -	static const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
>>>>>> -		{ ColorSpace::Raw, "RAW" },
>>>>>> -		{ ColorSpace::Srgb, "sRGB" },
>>>>>> -		{ ColorSpace::Sycc, "sYCC" },
>>>>>> -		{ ColorSpace::Smpte170m, "SMPTE170M" },
>>>>>> -		{ ColorSpace::Rec709, "Rec709" },
>>>>>> -		{ ColorSpace::Rec2020, "Rec2020" },
>>>>>> -	} };
>>>>>>     	auto it = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
>>>>>>     			       [this](const auto &item) {
>>>>>>     				       return *this == item.first;
>>>>>> @@ -240,28 +273,6 @@ std::string ColorSpace::toString() const
>>>>>>     
>>>>>>     	/* Assemble a name made of the constituent fields. */
>>>>>>     
>>>>>> -	static const std::map<Primaries, std::string> primariesNames = {
>>>>>> -		{ Primaries::Raw, "RAW" },
>>>>>> -		{ Primaries::Smpte170m, "SMPTE170M" },
>>>>>> -		{ Primaries::Rec709, "Rec709" },
>>>>>> -		{ Primaries::Rec2020, "Rec2020" },
>>>>>> -	};
>>>>>> -	static const std::map<TransferFunction, std::string> transferNames = {
>>>>>> -		{ TransferFunction::Linear, "Linear" },
>>>>>> -		{ TransferFunction::Srgb, "sRGB" },
>>>>>> -		{ TransferFunction::Rec709, "Rec709" },
>>>>>> -	};
>>>>>> -	static const std::map<YcbcrEncoding, std::string> encodingNames = {
>>>>>> -		{ YcbcrEncoding::None, "None" },
>>>>>> -		{ YcbcrEncoding::Rec601, "Rec601" },
>>>>>> -		{ YcbcrEncoding::Rec709, "Rec709" },
>>>>>> -		{ YcbcrEncoding::Rec2020, "Rec2020" },
>>>>>> -	};
>>>>>> -	static const std::map<Range, std::string> rangeNames = {
>>>>>> -		{ Range::Full, "Full" },
>>>>>> -		{ Range::Limited, "Limited" },
>>>>>> -	};
>>>>>> -
>>>>>>     	auto itPrimaries = primariesNames.find(primaries);
>>>>>>     	std::string primariesName =
>>>>>>     		itPrimaries == primariesNames.end() ? "Invalid" : itPrimaries->second;
>>>>>> @@ -303,6 +314,84 @@ std::string ColorSpace::toString(const std::optional<ColorSpace> &colorSpace)
>>>>>>     	return colorSpace->toString();
>>>>>>     }
>>>>>>     
>>>>>> +/**
>>>>>> + * \brief Construct a color space from a string
>>>>>> + * \param[in] str The string
>>>>>> + *
>>>>>> + * The string \a str can contain the name of a well-known color space, or be
>>>>>> + * made of the four color space components separate by a '/' character. Any
>>>>>> + * failure to parse the string, either because it doesn't match the expected
>>>>>> + * format, or because the one of the names isn't recognized, will cause this
>>>>>> + * function to return std::nullopt.
>>>>>> + *
>>>>>> + * \return The ColorSpace corresponding to the string, or std::nullopt if the
>>>>>> + * string doesn't describe a known color space
>>>>>> + */
>>>>>> +std::optional<ColorSpace> ColorSpace::fromString(const std::string &str)
>>>>>> +{
>>>>>> +	/* First search for a standard color space name match. */
>>>>>> +	auto itColorSpace = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
>>>>>> +					 [&str](const auto &item) {
>>>>>> +						 return str == item.second;
>>>>>> +					 });
>>>>>> +	if (itColorSpace != colorSpaceNames.end())
>>>>>> +		return itColorSpace->first;
>>>>>> +
>>>>>> +	/*
>>>>>> +	 * If not found, the string must contain the four color space
>>>>>> +	 * components separated by a '/' character.
>>>>>> +	 */
>>>>>> +	const auto &split = utils::split(str, "/");
>>>>>> +	std::vector<std::string> components{ split.begin(), split.end() };
>>>>>> +
>>>>>> +	if (components.size() != 4)
>>>>>> +		return std::nullopt;
>>>>>> +
>>>>>> +	ColorSpace colorSpace = ColorSpace::Raw;
>>>>>> +
>>>>>> +	/* Color primaries */
>>>>>> +	auto itPrimaries = std::find_if(primariesNames.begin(), primariesNames.end(),
>>>>>> +					[&components](const auto &item) {
>>>>>> +						return components[0] == item.second;
>>>>>> +					});
>>>>>> +	if (itPrimaries == primariesNames.end())
>>>>>> +		return std::nullopt;
>>>>>> +
>>>>>> +	colorSpace.primaries = itPrimaries->first;
>>>>>> +
>>>>>> +	/* Transfer function */
>>>>>> +	auto itTransfer = std::find_if(transferNames.begin(), transferNames.end(),
>>>>>> +				       [&components](const auto &item) {
>>>>>> +					       return components[1] == item.second;
>>>>>> +				       });
>>>>>> +	if (itTransfer == transferNames.end())
>>>>>> +		return std::nullopt;
>>>>>> +
>>>>>> +	colorSpace.transferFunction = itTransfer->first;
>>>>>> +
>>>>>> +	/* YCbCr encoding */
>>>>>> +	auto itEncoding = std::find_if(encodingNames.begin(), encodingNames.end(),
>>>>>> +				       [&components](const auto &item) {
>>>>>> +					       return components[2] == item.second;
>>>>>> +				       });
>>>>>> +	if (itEncoding == encodingNames.end())
>>>>>> +		return std::nullopt;
>>>>>> +
>>>>>> +	colorSpace.ycbcrEncoding = itEncoding->first;
>>>>>> +
>>>>>> +	/* Quantization range */
>>>>>> +	auto itRange = std::find_if(rangeNames.begin(), rangeNames.end(),
>>>>>> +				    [&components](const auto &item) {
>>>>>> +					    return components[3] == item.second;
>>>>>> +				    });
>>>>>> +	if (itRange == rangeNames.end())
>>>>>> +		return std::nullopt;
>>>>>> +
>>>>>> +	colorSpace.range = itRange->first;
>>>>>> +
>>>>>> +	return colorSpace;
>>>>>> +}
>>>>>> +
>>>>>>     /**
>>>>>>      * \brief Compare color spaces for equality
>>>>>>      * \return True if the two color spaces are identical, false otherwise
Laurent Pinchart Aug. 25, 2022, 3:51 p.m. UTC | #7
On Thu, Aug 25, 2022 at 09:13:02PM +0530, Umang Jain wrote:
> On 8/25/22 8:06 PM, Laurent Pinchart wrote:
> > On Thu, Aug 25, 2022 at 06:49:53PM +0530, Umang Jain wrote:
> >> On 8/25/22 3:02 AM, Laurent Pinchart wrote:
> >>> On Wed, Aug 24, 2022 at 12:40:59PM +0300, Laurent Pinchart via libcamera-devel wrote:
> >>>> On Wed, Aug 24, 2022 at 12:07:59PM +0530, Umang Jain wrote:
> >>>>> On 8/23/22 11:13 PM, Laurent Pinchart via libcamera-devel wrote:
> >>>>>> Add a ColorSpace:fromString() function to parse a string into a color
> >>>>>> space. The string can either contain the name of a well-known color
> >>>>>> space, or four color space components separate by a '/' character.
> >>>>> 
> >>>>> I don't see the order of four components separated by '/' - documented
> >>>>> in the codebase.
> >>>>> Should we document the string order as well?
> >>>>>
> >>>>> '''
> >>>>> The string representation of the colorspace separated by '/' is as follows:
> >>>>>        <Color primaries>/<Transfer function>/<Y'CbCr Encoding>/<Range>
> >>>>> '''
> >>>> 
> >>>> I'll expand the documentation of the function to make this more
> >>>> explicit.
> >> 
> >> Thank you!
> >>
> >>>> I'm also wondering (warning, bikeshedding ahead) if the '/'-separated
> >>>> format is the best option. GStreamer uses colons to separate the
> >>>> components, and only accepts numerical values for the components in that
> >>>> case. I'm not aware of (but haven't looked for) alternatives.
> >> 
> >> Oh, so we can't really align with gstreamer using the colon(':') because
> >> it would split awkwardly with utils::split(str, ":")  I think
> >>
> >> ColorSpace::Primaries::Rec709:ColorSpace::TransferFunction::Rec709:ColorSpace::YcbcrEncoding::Rec709:ColorSpace::Range::Full
> > 
> > The string doesn't have to fully qualify the components, it would be
> >
> > 	Rec709:Rec709:Rec709:Full
> 
> Yes correct, it can be - Can we keep ":" as the splitter then?  ;-)

Should I then update toString() too ?

David, any opinion ?

> >> In that case, I think "/" is our best choice. Second best I think will
> >> be "|"
> >>
> >> I saw a few Colorspace::toString() randomly on the internet, but none
> >> were structural. They were mostly just meant to print it out for logs
> >> (and not treating as string input).
> >>
> >> I also saw DRM also has colorspaces but haven't came across a helper
> >> like we need. I am not sure if it's worth to look further! "/" is
> >> already a good starting point, given our requirements.
> >>
> >>> By the way, the '/'-separated format comes from toString(). If we want
> >>> to change it, we should update the toString() function as well.
> >> 
> >> Ack.
> >>
> >>>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >>>>> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
> >>>>>
> >>>>>> ---
> >>>>>>     include/libcamera/color_space.h |   2 +
> >>>>>>     src/libcamera/color_space.cpp   | 149 +++++++++++++++++++++++++-------
> >>>>>>     2 files changed, 121 insertions(+), 30 deletions(-)
> >>>>>>
> >>>>>> diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h
> >>>>>> index 8030a264c66f..f493f72d2db8 100644
> >>>>>> --- a/include/libcamera/color_space.h
> >>>>>> +++ b/include/libcamera/color_space.h
> >>>>>> @@ -59,6 +59,8 @@ public:
> >>>>>>     
> >>>>>>     	std::string toString() const;
> >>>>>>     	static std::string toString(const std::optional<ColorSpace> &colorSpace);
> >>>>>> +
> >>>>>> +	static std::optional<ColorSpace> fromString(const std::string &str);
> >>>>>>     };
> >>>>>>     
> >>>>>>     bool operator==(const ColorSpace &lhs, const ColorSpace &rhs);
> >>>>>> diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp
> >>>>>> index 1b2dd2404452..5233626f5ae9 100644
> >>>>>> --- a/src/libcamera/color_space.cpp
> >>>>>> +++ b/src/libcamera/color_space.cpp
> >>>>>> @@ -12,6 +12,9 @@
> >>>>>>     #include <map>
> >>>>>>     #include <sstream>
> >>>>>>     #include <utility>
> >>>>>> +#include <vector>
> >>>>>> +
> >>>>>> +#include <libcamera/base/utils.h>
> >>>>>>     
> >>>>>>     /**
> >>>>>>      * \file color_space.h
> >>>>>> @@ -208,6 +211,44 @@ const ColorSpace ColorSpace::Rec2020 = {
> >>>>>>      * \brief The pixel range used with by color space
> >>>>>>      */
> >>>>>>     
> >>>>>> +namespace {
> >>>>>> +
> >>>>>> +const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
> >>>>>> +	{ ColorSpace::Raw, "RAW" },
> >>>>>> +	{ ColorSpace::Srgb, "sRGB" },
> >>>>>> +	{ ColorSpace::Sycc, "sYCC" },
> >>>>>> +	{ ColorSpace::Smpte170m, "SMPTE170M" },
> >>>>>> +	{ ColorSpace::Rec709, "Rec709" },
> >>>>>> +	{ ColorSpace::Rec2020, "Rec2020" },
> >>>>>> +} };
> >>>>>> +
> >>>>>> +const std::map<ColorSpace::Primaries, std::string> primariesNames = {
> >>>>>> +	{ ColorSpace::Primaries::Raw, "RAW" },
> >>>>>> +	{ ColorSpace::Primaries::Smpte170m, "SMPTE170M" },
> >>>>>> +	{ ColorSpace::Primaries::Rec709, "Rec709" },
> >>>>>> +	{ ColorSpace::Primaries::Rec2020, "Rec2020" },
> >>>>>> +};
> >>>>>> +
> >>>>>> +const std::map<ColorSpace::TransferFunction, std::string> transferNames = {
> >>>>>> +	{ ColorSpace::TransferFunction::Linear, "Linear" },
> >>>>>> +	{ ColorSpace::TransferFunction::Srgb, "sRGB" },
> >>>>>> +	{ ColorSpace::TransferFunction::Rec709, "Rec709" },
> >>>>>> +};
> >>>>>> +
> >>>>>> +const std::map<ColorSpace::YcbcrEncoding, std::string> encodingNames = {
> >>>>>> +	{ ColorSpace::YcbcrEncoding::None, "None" },
> >>>>>> +	{ ColorSpace::YcbcrEncoding::Rec601, "Rec601" },
> >>>>>> +	{ ColorSpace::YcbcrEncoding::Rec709, "Rec709" },
> >>>>>> +	{ ColorSpace::YcbcrEncoding::Rec2020, "Rec2020" },
> >>>>>> +};
> >>>>>> +
> >>>>>> +const std::map<ColorSpace::Range, std::string> rangeNames = {
> >>>>>> +	{ ColorSpace::Range::Full, "Full" },
> >>>>>> +	{ ColorSpace::Range::Limited, "Limited" },
> >>>>>> +};
> >>>>>> +
> >>>>>> +} /* namespace */
> >>>>>> +
> >>>>>>     /**
> >>>>>>      * \brief Assemble and return a readable string representation of the
> >>>>>>      * ColorSpace
> >>>>>> @@ -223,14 +264,6 @@ std::string ColorSpace::toString() const
> >>>>>>     {
> >>>>>>     	/* Print out a brief name only for standard color spaces. */
> >>>>>>     
> >>>>>> -	static const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
> >>>>>> -		{ ColorSpace::Raw, "RAW" },
> >>>>>> -		{ ColorSpace::Srgb, "sRGB" },
> >>>>>> -		{ ColorSpace::Sycc, "sYCC" },
> >>>>>> -		{ ColorSpace::Smpte170m, "SMPTE170M" },
> >>>>>> -		{ ColorSpace::Rec709, "Rec709" },
> >>>>>> -		{ ColorSpace::Rec2020, "Rec2020" },
> >>>>>> -	} };
> >>>>>>     	auto it = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
> >>>>>>     			       [this](const auto &item) {
> >>>>>>     				       return *this == item.first;
> >>>>>> @@ -240,28 +273,6 @@ std::string ColorSpace::toString() const
> >>>>>>     
> >>>>>>     	/* Assemble a name made of the constituent fields. */
> >>>>>>     
> >>>>>> -	static const std::map<Primaries, std::string> primariesNames = {
> >>>>>> -		{ Primaries::Raw, "RAW" },
> >>>>>> -		{ Primaries::Smpte170m, "SMPTE170M" },
> >>>>>> -		{ Primaries::Rec709, "Rec709" },
> >>>>>> -		{ Primaries::Rec2020, "Rec2020" },
> >>>>>> -	};
> >>>>>> -	static const std::map<TransferFunction, std::string> transferNames = {
> >>>>>> -		{ TransferFunction::Linear, "Linear" },
> >>>>>> -		{ TransferFunction::Srgb, "sRGB" },
> >>>>>> -		{ TransferFunction::Rec709, "Rec709" },
> >>>>>> -	};
> >>>>>> -	static const std::map<YcbcrEncoding, std::string> encodingNames = {
> >>>>>> -		{ YcbcrEncoding::None, "None" },
> >>>>>> -		{ YcbcrEncoding::Rec601, "Rec601" },
> >>>>>> -		{ YcbcrEncoding::Rec709, "Rec709" },
> >>>>>> -		{ YcbcrEncoding::Rec2020, "Rec2020" },
> >>>>>> -	};
> >>>>>> -	static const std::map<Range, std::string> rangeNames = {
> >>>>>> -		{ Range::Full, "Full" },
> >>>>>> -		{ Range::Limited, "Limited" },
> >>>>>> -	};
> >>>>>> -
> >>>>>>     	auto itPrimaries = primariesNames.find(primaries);
> >>>>>>     	std::string primariesName =
> >>>>>>     		itPrimaries == primariesNames.end() ? "Invalid" : itPrimaries->second;
> >>>>>> @@ -303,6 +314,84 @@ std::string ColorSpace::toString(const std::optional<ColorSpace> &colorSpace)
> >>>>>>     	return colorSpace->toString();
> >>>>>>     }
> >>>>>>     
> >>>>>> +/**
> >>>>>> + * \brief Construct a color space from a string
> >>>>>> + * \param[in] str The string
> >>>>>> + *
> >>>>>> + * The string \a str can contain the name of a well-known color space, or be
> >>>>>> + * made of the four color space components separate by a '/' character. Any
> >>>>>> + * failure to parse the string, either because it doesn't match the expected
> >>>>>> + * format, or because the one of the names isn't recognized, will cause this
> >>>>>> + * function to return std::nullopt.
> >>>>>> + *
> >>>>>> + * \return The ColorSpace corresponding to the string, or std::nullopt if the
> >>>>>> + * string doesn't describe a known color space
> >>>>>> + */
> >>>>>> +std::optional<ColorSpace> ColorSpace::fromString(const std::string &str)
> >>>>>> +{
> >>>>>> +	/* First search for a standard color space name match. */
> >>>>>> +	auto itColorSpace = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
> >>>>>> +					 [&str](const auto &item) {
> >>>>>> +						 return str == item.second;
> >>>>>> +					 });
> >>>>>> +	if (itColorSpace != colorSpaceNames.end())
> >>>>>> +		return itColorSpace->first;
> >>>>>> +
> >>>>>> +	/*
> >>>>>> +	 * If not found, the string must contain the four color space
> >>>>>> +	 * components separated by a '/' character.
> >>>>>> +	 */
> >>>>>> +	const auto &split = utils::split(str, "/");
> >>>>>> +	std::vector<std::string> components{ split.begin(), split.end() };
> >>>>>> +
> >>>>>> +	if (components.size() != 4)
> >>>>>> +		return std::nullopt;
> >>>>>> +
> >>>>>> +	ColorSpace colorSpace = ColorSpace::Raw;
> >>>>>> +
> >>>>>> +	/* Color primaries */
> >>>>>> +	auto itPrimaries = std::find_if(primariesNames.begin(), primariesNames.end(),
> >>>>>> +					[&components](const auto &item) {
> >>>>>> +						return components[0] == item.second;
> >>>>>> +					});
> >>>>>> +	if (itPrimaries == primariesNames.end())
> >>>>>> +		return std::nullopt;
> >>>>>> +
> >>>>>> +	colorSpace.primaries = itPrimaries->first;
> >>>>>> +
> >>>>>> +	/* Transfer function */
> >>>>>> +	auto itTransfer = std::find_if(transferNames.begin(), transferNames.end(),
> >>>>>> +				       [&components](const auto &item) {
> >>>>>> +					       return components[1] == item.second;
> >>>>>> +				       });
> >>>>>> +	if (itTransfer == transferNames.end())
> >>>>>> +		return std::nullopt;
> >>>>>> +
> >>>>>> +	colorSpace.transferFunction = itTransfer->first;
> >>>>>> +
> >>>>>> +	/* YCbCr encoding */
> >>>>>> +	auto itEncoding = std::find_if(encodingNames.begin(), encodingNames.end(),
> >>>>>> +				       [&components](const auto &item) {
> >>>>>> +					       return components[2] == item.second;
> >>>>>> +				       });
> >>>>>> +	if (itEncoding == encodingNames.end())
> >>>>>> +		return std::nullopt;
> >>>>>> +
> >>>>>> +	colorSpace.ycbcrEncoding = itEncoding->first;
> >>>>>> +
> >>>>>> +	/* Quantization range */
> >>>>>> +	auto itRange = std::find_if(rangeNames.begin(), rangeNames.end(),
> >>>>>> +				    [&components](const auto &item) {
> >>>>>> +					    return components[3] == item.second;
> >>>>>> +				    });
> >>>>>> +	if (itRange == rangeNames.end())
> >>>>>> +		return std::nullopt;
> >>>>>> +
> >>>>>> +	colorSpace.range = itRange->first;
> >>>>>> +
> >>>>>> +	return colorSpace;
> >>>>>> +}
> >>>>>> +
> >>>>>>     /**
> >>>>>>      * \brief Compare color spaces for equality
> >>>>>>      * \return True if the two color spaces are identical, false otherwise
>

Patch
diff mbox series

diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h
index 8030a264c66f..f493f72d2db8 100644
--- a/include/libcamera/color_space.h
+++ b/include/libcamera/color_space.h
@@ -59,6 +59,8 @@  public:
 
 	std::string toString() const;
 	static std::string toString(const std::optional<ColorSpace> &colorSpace);
+
+	static std::optional<ColorSpace> fromString(const std::string &str);
 };
 
 bool operator==(const ColorSpace &lhs, const ColorSpace &rhs);
diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp
index 1b2dd2404452..5233626f5ae9 100644
--- a/src/libcamera/color_space.cpp
+++ b/src/libcamera/color_space.cpp
@@ -12,6 +12,9 @@ 
 #include <map>
 #include <sstream>
 #include <utility>
+#include <vector>
+
+#include <libcamera/base/utils.h>
 
 /**
  * \file color_space.h
@@ -208,6 +211,44 @@  const ColorSpace ColorSpace::Rec2020 = {
  * \brief The pixel range used with by color space
  */
 
+namespace {
+
+const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
+	{ ColorSpace::Raw, "RAW" },
+	{ ColorSpace::Srgb, "sRGB" },
+	{ ColorSpace::Sycc, "sYCC" },
+	{ ColorSpace::Smpte170m, "SMPTE170M" },
+	{ ColorSpace::Rec709, "Rec709" },
+	{ ColorSpace::Rec2020, "Rec2020" },
+} };
+
+const std::map<ColorSpace::Primaries, std::string> primariesNames = {
+	{ ColorSpace::Primaries::Raw, "RAW" },
+	{ ColorSpace::Primaries::Smpte170m, "SMPTE170M" },
+	{ ColorSpace::Primaries::Rec709, "Rec709" },
+	{ ColorSpace::Primaries::Rec2020, "Rec2020" },
+};
+
+const std::map<ColorSpace::TransferFunction, std::string> transferNames = {
+	{ ColorSpace::TransferFunction::Linear, "Linear" },
+	{ ColorSpace::TransferFunction::Srgb, "sRGB" },
+	{ ColorSpace::TransferFunction::Rec709, "Rec709" },
+};
+
+const std::map<ColorSpace::YcbcrEncoding, std::string> encodingNames = {
+	{ ColorSpace::YcbcrEncoding::None, "None" },
+	{ ColorSpace::YcbcrEncoding::Rec601, "Rec601" },
+	{ ColorSpace::YcbcrEncoding::Rec709, "Rec709" },
+	{ ColorSpace::YcbcrEncoding::Rec2020, "Rec2020" },
+};
+
+const std::map<ColorSpace::Range, std::string> rangeNames = {
+	{ ColorSpace::Range::Full, "Full" },
+	{ ColorSpace::Range::Limited, "Limited" },
+};
+
+} /* namespace */
+
 /**
  * \brief Assemble and return a readable string representation of the
  * ColorSpace
@@ -223,14 +264,6 @@  std::string ColorSpace::toString() const
 {
 	/* Print out a brief name only for standard color spaces. */
 
-	static const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {
-		{ ColorSpace::Raw, "RAW" },
-		{ ColorSpace::Srgb, "sRGB" },
-		{ ColorSpace::Sycc, "sYCC" },
-		{ ColorSpace::Smpte170m, "SMPTE170M" },
-		{ ColorSpace::Rec709, "Rec709" },
-		{ ColorSpace::Rec2020, "Rec2020" },
-	} };
 	auto it = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
 			       [this](const auto &item) {
 				       return *this == item.first;
@@ -240,28 +273,6 @@  std::string ColorSpace::toString() const
 
 	/* Assemble a name made of the constituent fields. */
 
-	static const std::map<Primaries, std::string> primariesNames = {
-		{ Primaries::Raw, "RAW" },
-		{ Primaries::Smpte170m, "SMPTE170M" },
-		{ Primaries::Rec709, "Rec709" },
-		{ Primaries::Rec2020, "Rec2020" },
-	};
-	static const std::map<TransferFunction, std::string> transferNames = {
-		{ TransferFunction::Linear, "Linear" },
-		{ TransferFunction::Srgb, "sRGB" },
-		{ TransferFunction::Rec709, "Rec709" },
-	};
-	static const std::map<YcbcrEncoding, std::string> encodingNames = {
-		{ YcbcrEncoding::None, "None" },
-		{ YcbcrEncoding::Rec601, "Rec601" },
-		{ YcbcrEncoding::Rec709, "Rec709" },
-		{ YcbcrEncoding::Rec2020, "Rec2020" },
-	};
-	static const std::map<Range, std::string> rangeNames = {
-		{ Range::Full, "Full" },
-		{ Range::Limited, "Limited" },
-	};
-
 	auto itPrimaries = primariesNames.find(primaries);
 	std::string primariesName =
 		itPrimaries == primariesNames.end() ? "Invalid" : itPrimaries->second;
@@ -303,6 +314,84 @@  std::string ColorSpace::toString(const std::optional<ColorSpace> &colorSpace)
 	return colorSpace->toString();
 }
 
+/**
+ * \brief Construct a color space from a string
+ * \param[in] str The string
+ *
+ * The string \a str can contain the name of a well-known color space, or be
+ * made of the four color space components separate by a '/' character. Any
+ * failure to parse the string, either because it doesn't match the expected
+ * format, or because the one of the names isn't recognized, will cause this
+ * function to return std::nullopt.
+ *
+ * \return The ColorSpace corresponding to the string, or std::nullopt if the
+ * string doesn't describe a known color space
+ */
+std::optional<ColorSpace> ColorSpace::fromString(const std::string &str)
+{
+	/* First search for a standard color space name match. */
+	auto itColorSpace = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),
+					 [&str](const auto &item) {
+						 return str == item.second;
+					 });
+	if (itColorSpace != colorSpaceNames.end())
+		return itColorSpace->first;
+
+	/*
+	 * If not found, the string must contain the four color space
+	 * components separated by a '/' character.
+	 */
+	const auto &split = utils::split(str, "/");
+	std::vector<std::string> components{ split.begin(), split.end() };
+
+	if (components.size() != 4)
+		return std::nullopt;
+
+	ColorSpace colorSpace = ColorSpace::Raw;
+
+	/* Color primaries */
+	auto itPrimaries = std::find_if(primariesNames.begin(), primariesNames.end(),
+					[&components](const auto &item) {
+						return components[0] == item.second;
+					});
+	if (itPrimaries == primariesNames.end())
+		return std::nullopt;
+
+	colorSpace.primaries = itPrimaries->first;
+
+	/* Transfer function */
+	auto itTransfer = std::find_if(transferNames.begin(), transferNames.end(),
+				       [&components](const auto &item) {
+					       return components[1] == item.second;
+				       });
+	if (itTransfer == transferNames.end())
+		return std::nullopt;
+
+	colorSpace.transferFunction = itTransfer->first;
+
+	/* YCbCr encoding */
+	auto itEncoding = std::find_if(encodingNames.begin(), encodingNames.end(),
+				       [&components](const auto &item) {
+					       return components[2] == item.second;
+				       });
+	if (itEncoding == encodingNames.end())
+		return std::nullopt;
+
+	colorSpace.ycbcrEncoding = itEncoding->first;
+
+	/* Quantization range */
+	auto itRange = std::find_if(rangeNames.begin(), rangeNames.end(),
+				    [&components](const auto &item) {
+					    return components[3] == item.second;
+				    });
+	if (itRange == rangeNames.end())
+		return std::nullopt;
+
+	colorSpace.range = itRange->first;
+
+	return colorSpace;
+}
+
 /**
  * \brief Compare color spaces for equality
  * \return True if the two color spaces are identical, false otherwise