[libcamera-devel,v3,1/7] libcamera: Add ColorSpace class
diff mbox series

Message ID 20211020110825.12902-2-david.plowman@raspberrypi.com
State Superseded
Headers show
Series
  • Colour spaces
Related show

Commit Message

David Plowman Oct. 20, 2021, 11:08 a.m. UTC
This class represents a color space by defining its color primaries,
YCbCr encoding, the transfer (gamma) function it uses, and whether the
output is full or limited range.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
---
 include/libcamera/color_space.h |  88 +++++++++++
 include/libcamera/meson.build   |   1 +
 src/libcamera/color_space.cpp   | 256 ++++++++++++++++++++++++++++++++
 src/libcamera/meson.build       |   1 +
 4 files changed, 346 insertions(+)
 create mode 100644 include/libcamera/color_space.h
 create mode 100644 src/libcamera/color_space.cpp

Comments

Naushir Patuck Oct. 25, 2021, 7:36 a.m. UTC | #1
Hi David,

Thank you for your work.

On Wed, 20 Oct 2021 at 12:08, David Plowman <david.plowman@raspberrypi.com>
wrote:

> This class represents a color space by defining its color primaries,
> YCbCr encoding, the transfer (gamma) function it uses, and whether the
> output is full or limited range.
>
> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> ---
>  include/libcamera/color_space.h |  88 +++++++++++
>  include/libcamera/meson.build   |   1 +
>  src/libcamera/color_space.cpp   | 256 ++++++++++++++++++++++++++++++++
>  src/libcamera/meson.build       |   1 +
>  4 files changed, 346 insertions(+)
>  create mode 100644 include/libcamera/color_space.h
>  create mode 100644 src/libcamera/color_space.cpp
>
> diff --git a/include/libcamera/color_space.h
> b/include/libcamera/color_space.h
> new file mode 100644
> index 00000000..2af9da31
> --- /dev/null
> +++ b/include/libcamera/color_space.h
> @@ -0,0 +1,88 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021, Raspberry Pi (Trading) Limited
> + *
> + * color_space.h - color space definitions
> + */
> +
> +#ifndef __LIBCAMERA_COLOR_SPACE_H__
> +#define __LIBCAMERA_COLOR_SPACE_H__
> +
> +#include <string>
> +
> +namespace libcamera {
> +
> +class ColorSpace
> +{
> +public:
> +       enum class Primaries : int {
> +               Undefined,
> +               Raw,
> +               Smpte170m,
> +               Rec709,
> +               Rec2020,
> +       };
> +
> +       enum class YcbcrEncoding : int {
> +               Undefined,
> +               Rec601,
> +               Rec709,
> +               Rec2020,
> +       };
> +
> +       enum class TransferFunction : int {
> +               Undefined,
> +               Linear,
> +               Srgb,
> +               Rec709,
> +       };
> +
> +       enum class Range : int {
> +               Undefined,
> +               Full,
> +               Limited,
> +       };
> +
> +       constexpr ColorSpace()
> +               : ColorSpace(Primaries::Undefined,
> YcbcrEncoding::Undefined, TransferFunction::Undefined, Range::Undefined)
> +       {
> +       }
> +
> +       constexpr ColorSpace(Primaries p, YcbcrEncoding e,
> TransferFunction t, Range r)
> +               : primaries(p), ycbcrEncoding(e), transferFunction(t),
> range(r)
> +       {
> +       }
> +
> +       static const ColorSpace Undefined;
> +       static const ColorSpace Raw;
> +       static const ColorSpace Jpeg;
> +       static const ColorSpace Smpte170m;
> +       static const ColorSpace Rec709;
> +       static const ColorSpace Rec2020;
> +
> +       Primaries primaries;
> +       YcbcrEncoding ycbcrEncoding;
> +       TransferFunction transferFunction;
> +       Range range;
> +
> +       bool isFullyDefined() const;
>

Other classes seem to use an isValid() member function. Should we do the
same here?
Granted, this is not exactly the same, a false return value from
isFullyDefined() might be
allowed for a valid colourspace?



> +
> +       const std::string toString() const;
> +};
> +
> +constexpr ColorSpace ColorSpace::Undefined = { Primaries::Undefined,
> YcbcrEncoding::Undefined, TransferFunction::Undefined, Range::Undefined };
> +constexpr ColorSpace ColorSpace::Raw = { Primaries::Raw,
> YcbcrEncoding::Rec601, TransferFunction::Linear, Range::Full };
> +constexpr ColorSpace ColorSpace::Jpeg = { Primaries::Rec709,
> YcbcrEncoding::Rec601, TransferFunction::Srgb, Range::Full };
> +constexpr ColorSpace ColorSpace::Smpte170m = { Primaries::Smpte170m,
> YcbcrEncoding::Rec601, TransferFunction::Rec709, Range::Limited };
> +constexpr ColorSpace ColorSpace::Rec709 = { Primaries::Rec709,
> YcbcrEncoding::Rec709, TransferFunction::Rec709, Range::Limited };
> +constexpr ColorSpace ColorSpace::Rec2020 = { Primaries::Rec2020,
> YcbcrEncoding::Rec2020, TransferFunction::Rec709, Range::Limited };
> +
> +bool operator==(const ColorSpace &lhs, const ColorSpace &rhs);
> +static inline bool operator!=(const ColorSpace &lhs, const ColorSpace
> &rhs)
> +{
> +       return !(lhs == rhs);
> +}
> +
> +} /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_COLOR_SPACE_H__ */
> diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build
> index 7155ff20..131e1740 100644
> --- a/include/libcamera/meson.build
> +++ b/include/libcamera/meson.build
> @@ -5,6 +5,7 @@ libcamera_include_dir = 'libcamera' / 'libcamera'
>  libcamera_public_headers = files([
>      'camera.h',
>      'camera_manager.h',
> +    'color_space.h',
>      'compiler.h',
>      'controls.h',
>      'file_descriptor.h',
> diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp
> new file mode 100644
> index 00000000..aec4107f
> --- /dev/null
> +++ b/src/libcamera/color_space.cpp
> @@ -0,0 +1,256 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021, Raspberry Pi (Trading) Limited
> + *
> + * color_space.cpp - color spaces.
> + */
> +
> +#include <libcamera/color_space.h>
> +
> +#include <algorithm>
> +#include <sstream>
> +#include <vector>
> +
> +/**
> + * \file color_space.h
> + * \brief Class and enums to represent color spaces
> + */
> +
> +namespace libcamera {
> +
> +/**
> + * \class ColorSpace
> + * \brief Class to describe a color space
> + *
> + * The ColorSpace class defines the color primaries, the Y'CbCr encoding,
> + * the transfer function associated with the color space, and the range
> + * (sometimes also referred to as the quantisation) of the color space.
> + *
> + * Certain combinations of these fields form well-known standard color
> + * spaces such as "JPEG" or "REC709". Applications must not request color
> + * spaces with undefined fields, but the "Undefined" value may be
> + * returned if the camera drivers decide to use a color space that is
> + * not recognised by the ColorSpace class.
> + *
> + * For more information on the specific color spaces described here,
> please
> + * see:
> + *
> + * <a href="
> https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-srgb">sRGB</a>
> and <a href="
> https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-jpeg
> ">JPEG</a>
> +>* <a href="
> https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-smpte-170m">SMPTE
> 170M</a>
> + * <a href="
> https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-rec709
> ">Rec.709</a>
> + * <a href="
> https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-bt2020
> ">Rec.2020</a>
> + */
> +
> +/**
> + * \enum ColorSpace::Primaries
> + * \brief The color primaries for this color space
> + *
> + * \var ColorSpace::Primaries::Undefined
> + * \brief The color primaries are undefined
> + * \var ColorSpace::Primaries::Raw
> + * \brief These are raw colors directly from a sensor
> + * \var ColorSpace::Primaries::Smpte170m
> + * \brief SMPTE 170M color primaries
> + * \var ColorSpace::Primaries::Rec709
> + * \brief Rec.709 color primaries
> + * \var ColorSpace::Primaries::Rec2020
> + * \brief Rec.2020 color primaries
> + */
> +
> +/**
> + * \enum ColorSpace::YcbcrEncoding
> + * \brief The Y'CbCr encoding
> + *
> + * \var ColorSpace::YcbcrEncoding::Undefined
> + * \brief The Y'CbCr encoding is undefined
> + * \var ColorSpace::YcbcrEncoding::Rec601
> + * \brief Rec.601 Y'CbCr encoding
> + * \var ColorSpace::YcbcrEncoding::Rec709
> + * \brief Rec.709 Y'CbCr encoding
> + * \var ColorSpace::YcbcrEncoding::Rec2020
> + * \brief Rec.2020 Y'CbCr encoding
> + */
> +
> +/**
> + * \enum ColorSpace::TransferFunction
> + * \brief The transfer function used for this color space
> + *
> + * \var ColorSpace::TransferFunction::Undefined
> + * \brief The transfer function is not specified
> + * \var ColorSpace::TransferFunction::Linear
> + * \brief This color space uses a linear (identity) transfer function
> + * \var ColorSpace::TransferFunction::Srgb
> + * \brief sRGB transfer function
> + * \var ColorSpace::TransferFunction::Rec709
> + * \brief Rec709 transfer function
> + */
> +
> +/**
> + * \enum ColorSpace::Range
> + * \brief The range (sometimes "quantisation") for this color space
> + *
> + * \var ColorSpace::Range::Undefined
> + * \brief The range is not specified
> + * \var ColorSpace::Range::Full
> + * \brief This color space uses full range pixel values
> + * \var ColorSpace::Range::Limited
> + * \brief This color space uses limited range pixel values, being
> + * 16 to 235 for Y' and 16 to 240 for Cb and Cr
>

Perhaps add the range for 10-bit as well?


> + */
> +
> +/**
> + * \fn ColorSpace::ColorSpace(Encoding e, TransferFunction t, Range r)
> + * \brief Construct a ColorSpace from explicit values
> + * \param[in] e The Y'CbCr encoding
> + * \param[in] t The transfer function for the color space
> + * \param[in] r The range of the pixel values in this color space
> + */
> +
> +/**
> + * \fn ColorSpace::ColorSpace()
> + * \brief Construct a color space with undefined encoding, transfer
> function
> + * and range
> + */
> +
> +/**
> + * \brief Check if all the fields of the color space are defined
> + * \return Return true if all the fields of the color space are defined,
> + * otherwise false
> + */
> +bool ColorSpace::isFullyDefined() const
> +{
> +       return primaries != Primaries::Undefined &&
> +              ycbcrEncoding != YcbcrEncoding::Undefined &&
> +              transferFunction != TransferFunction::Undefined &&
> +              range != Range::Undefined;
> +}
> +
> +/**
> + * \brief Assemble and return a readable string representation of the
> + * ColorSpace
> + * \return A string describing the ColorSpace
> + */
> +const std::string ColorSpace::toString() const
> +{
> +       /* Print out a brief name only for standard color sapces. */
> +
> +       static const std::vector<std::pair<ColorSpace, const char *>>
> colorSpaceNames = {
> +               { ColorSpace::Undefined, "Undefined" },
> +               { ColorSpace::Raw, "Raw" },
> +               { ColorSpace::Jpeg, "Jpeg" },
> +               { 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;
> +                              });
>

Perhaps this might be better done with a std::map or even embedding the
string
into a member variable?  Not too fussed either way.

Reviewed-by: Naushir Patuck <naush@raspberrypi.com>


> +       if (it != colorSpaceNames.end())
> +               return std::string(it->second);
> +
> +       static const char *primariesNames[] = {
> +               "Undefined",
> +               "Raw",
> +               "Smpte170m",
> +               "Rec709",
> +               "Rec2020",
> +       };
> +       static const char *encodingNames[] = {
> +               "Undefined",
> +               "Rec601",
> +               "Rec709",
> +               "Rec2020",
> +       };
> +       static const char *transferFunctionNames[] = {
> +               "Undefined",
> +               "Linear",
> +               "Srgb",
> +               "Rec709",
> +       };
> +       static const char *rangeNames[] = {
> +               "Undefined",
> +               "Full",
> +               "Limited",
> +       };
> +
> +       std::stringstream ss;
> +       ss << std::string(primariesNames[static_cast<int>(primaries)]) <<
> "/"
> +          << std::string(encodingNames[static_cast<int>(ycbcrEncoding)])
> << "/"
> +          <<
> std::string(transferFunctionNames[static_cast<int>(transferFunction)]) <<
> "/"
> +          << std::string(rangeNames[static_cast<int>(range)]);
> +
> +       return ss.str();
> +}
> +
> +/**
> + * \var ColorSpace::primaries
> + * \brief The color primaries
> + */
> +
> +/**
> + * \var ColorSpace::ycbcrEncoding
> + * \brief The Y'CbCr encoding
> + */
> +
> +/**
> + * \var ColorSpace::transferFunction
> + * \brief The transfer function for this color space
> + */
> +
> +/**
> + * \var ColorSpace::range
> + * \brief The pixel range used by this color space
> + */
> +
> +/**
> + * \var ColorSpace::Undefined
> + * \brief A constant representing a fully undefined color space
> + */
> +
> +/**
> + * \var ColorSpace::Raw
> + * \brief A constant representing a raw color space (from a sensor)
> + */
> +
> +/**
> + * \var ColorSpace::Jpeg
> + * \brief A constant representing the JPEG color space used for
> + * encoding JPEG images (and regarded as being the same as the sRGB
> + * color space)
> + */
> +
> +/**
> + * \var ColorSpace::Smpte170m
> + * \brief A constant representing the SMPTE170M color space
> + */
> +
> +/**
> + * \var ColorSpace::Rec709
> + * \brief A constant representing the Rec.709 color space
> + */
> +
> +/**
> + * \var ColorSpace::Rec2020
> + * \brief A constant representing the Rec.2020 color space
> + */
> +
> +/**
> + * \brief Compare color spaces for equality
> + * \return True if the two color spaces are identical, false otherwise
> + */
> +bool operator==(const ColorSpace &lhs, const ColorSpace &rhs)
> +{
> +       return lhs.primaries == rhs.primaries &&
> +              lhs.ycbcrEncoding == rhs.ycbcrEncoding &&
> +              lhs.transferFunction == rhs.transferFunction &&
> +              lhs.range == rhs.range;
> +}
> +
> +/**
> + * \fn bool operator!=(const ColorSpace &lhs, const ColorSpace &rhs)
> + * \brief Compare color spaces for inequality
> + * \return True if the two color spaces are not identical, false otherwise
> + */
> +
> +} /* namespace libcamera */
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index 243dd3c1..8dc5d39d 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -8,6 +8,7 @@ libcamera_sources = files([
>      'camera_manager.cpp',
>      'camera_sensor.cpp',
>      'camera_sensor_properties.cpp',
> +    'color_space.cpp',
>      'controls.cpp',
>      'control_serializer.cpp',
>      'control_validator.cpp',
> --
> 2.20.1
>
>
David Plowman Oct. 25, 2021, 9:27 a.m. UTC | #2
Hi Naush

Thanks for all the reviews, obviously I'll make up a v4 shortly!

On Mon, 25 Oct 2021 at 08:36, Naushir Patuck <naush@raspberrypi.com> wrote:
>
> Hi David,
>
> Thank you for your work.
>
> On Wed, 20 Oct 2021 at 12:08, David Plowman <david.plowman@raspberrypi.com> wrote:
>>
>> This class represents a color space by defining its color primaries,
>> YCbCr encoding, the transfer (gamma) function it uses, and whether the
>> output is full or limited range.
>>
>> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
>> ---
>>  include/libcamera/color_space.h |  88 +++++++++++
>>  include/libcamera/meson.build   |   1 +
>>  src/libcamera/color_space.cpp   | 256 ++++++++++++++++++++++++++++++++
>>  src/libcamera/meson.build       |   1 +
>>  4 files changed, 346 insertions(+)
>>  create mode 100644 include/libcamera/color_space.h
>>  create mode 100644 src/libcamera/color_space.cpp
>>
>> diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h
>> new file mode 100644
>> index 00000000..2af9da31
>> --- /dev/null
>> +++ b/include/libcamera/color_space.h
>> @@ -0,0 +1,88 @@
>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>> +/*
>> + * Copyright (C) 2021, Raspberry Pi (Trading) Limited
>> + *
>> + * color_space.h - color space definitions
>> + */
>> +
>> +#ifndef __LIBCAMERA_COLOR_SPACE_H__
>> +#define __LIBCAMERA_COLOR_SPACE_H__
>> +
>> +#include <string>
>> +
>> +namespace libcamera {
>> +
>> +class ColorSpace
>> +{
>> +public:
>> +       enum class Primaries : int {
>> +               Undefined,
>> +               Raw,
>> +               Smpte170m,
>> +               Rec709,
>> +               Rec2020,
>> +       };
>> +
>> +       enum class YcbcrEncoding : int {
>> +               Undefined,
>> +               Rec601,
>> +               Rec709,
>> +               Rec2020,
>> +       };
>> +
>> +       enum class TransferFunction : int {
>> +               Undefined,
>> +               Linear,
>> +               Srgb,
>> +               Rec709,
>> +       };
>> +
>> +       enum class Range : int {
>> +               Undefined,
>> +               Full,
>> +               Limited,
>> +       };
>> +
>> +       constexpr ColorSpace()
>> +               : ColorSpace(Primaries::Undefined, YcbcrEncoding::Undefined, TransferFunction::Undefined, Range::Undefined)
>> +       {
>> +       }
>> +
>> +       constexpr ColorSpace(Primaries p, YcbcrEncoding e, TransferFunction t, Range r)
>> +               : primaries(p), ycbcrEncoding(e), transferFunction(t), range(r)
>> +       {
>> +       }
>> +
>> +       static const ColorSpace Undefined;
>> +       static const ColorSpace Raw;
>> +       static const ColorSpace Jpeg;
>> +       static const ColorSpace Smpte170m;
>> +       static const ColorSpace Rec709;
>> +       static const ColorSpace Rec2020;
>> +
>> +       Primaries primaries;
>> +       YcbcrEncoding ycbcrEncoding;
>> +       TransferFunction transferFunction;
>> +       Range range;
>> +
>> +       bool isFullyDefined() const;
>
>
> Other classes seem to use an isValid() member function. Should we do the same here?
> Granted, this is not exactly the same, a false return value from isFullyDefined() might be
> allowed for a valid colourspace?

I actually prefer isFullyDefined ("are any fields undefined or not?")
because "valid" seems like a slightly slippery concept here. You could
have a driver choose a valid colour space but which the class doesn't
recognise. Is that "valid"? I'm not sure...

>
>
>>
>> +
>> +       const std::string toString() const;
>> +};
>> +
>> +constexpr ColorSpace ColorSpace::Undefined = { Primaries::Undefined, YcbcrEncoding::Undefined, TransferFunction::Undefined, Range::Undefined };
>> +constexpr ColorSpace ColorSpace::Raw = { Primaries::Raw, YcbcrEncoding::Rec601, TransferFunction::Linear, Range::Full };
>> +constexpr ColorSpace ColorSpace::Jpeg = { Primaries::Rec709, YcbcrEncoding::Rec601, TransferFunction::Srgb, Range::Full };
>> +constexpr ColorSpace ColorSpace::Smpte170m = { Primaries::Smpte170m, YcbcrEncoding::Rec601, TransferFunction::Rec709, Range::Limited };
>> +constexpr ColorSpace ColorSpace::Rec709 = { Primaries::Rec709, YcbcrEncoding::Rec709, TransferFunction::Rec709, Range::Limited };
>> +constexpr ColorSpace ColorSpace::Rec2020 = { Primaries::Rec2020, YcbcrEncoding::Rec2020, TransferFunction::Rec709, Range::Limited };
>> +
>> +bool operator==(const ColorSpace &lhs, const ColorSpace &rhs);
>> +static inline bool operator!=(const ColorSpace &lhs, const ColorSpace &rhs)
>> +{
>> +       return !(lhs == rhs);
>> +}
>> +
>> +} /* namespace libcamera */
>> +
>> +#endif /* __LIBCAMERA_COLOR_SPACE_H__ */
>> diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build
>> index 7155ff20..131e1740 100644
>> --- a/include/libcamera/meson.build
>> +++ b/include/libcamera/meson.build
>> @@ -5,6 +5,7 @@ libcamera_include_dir = 'libcamera' / 'libcamera'
>>  libcamera_public_headers = files([
>>      'camera.h',
>>      'camera_manager.h',
>> +    'color_space.h',
>>      'compiler.h',
>>      'controls.h',
>>      'file_descriptor.h',
>> diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp
>> new file mode 100644
>> index 00000000..aec4107f
>> --- /dev/null
>> +++ b/src/libcamera/color_space.cpp
>> @@ -0,0 +1,256 @@
>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
>> +/*
>> + * Copyright (C) 2021, Raspberry Pi (Trading) Limited
>> + *
>> + * color_space.cpp - color spaces.
>> + */
>> +
>> +#include <libcamera/color_space.h>
>> +
>> +#include <algorithm>
>> +#include <sstream>
>> +#include <vector>
>> +
>> +/**
>> + * \file color_space.h
>> + * \brief Class and enums to represent color spaces
>> + */
>> +
>> +namespace libcamera {
>> +
>> +/**
>> + * \class ColorSpace
>> + * \brief Class to describe a color space
>> + *
>> + * The ColorSpace class defines the color primaries, the Y'CbCr encoding,
>> + * the transfer function associated with the color space, and the range
>> + * (sometimes also referred to as the quantisation) of the color space.
>> + *
>> + * Certain combinations of these fields form well-known standard color
>> + * spaces such as "JPEG" or "REC709". Applications must not request color
>> + * spaces with undefined fields, but the "Undefined" value may be
>> + * returned if the camera drivers decide to use a color space that is
>> + * not recognised by the ColorSpace class.
>> + *
>> + * For more information on the specific color spaces described here, please
>> + * see:
>> + *
>> + * <a href="https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-srgb">sRGB</a> and <a href="https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-jpeg">JPEG</a>
>> +>* <a href="https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-smpte-170m">SMPTE 170M</a>
>> + * <a href="https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-rec709">Rec.709</a>
>> + * <a href="https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-bt2020">Rec.2020</a>
>> + */
>> +
>> +/**
>> + * \enum ColorSpace::Primaries
>> + * \brief The color primaries for this color space
>> + *
>> + * \var ColorSpace::Primaries::Undefined
>> + * \brief The color primaries are undefined
>> + * \var ColorSpace::Primaries::Raw
>> + * \brief These are raw colors directly from a sensor
>> + * \var ColorSpace::Primaries::Smpte170m
>> + * \brief SMPTE 170M color primaries
>> + * \var ColorSpace::Primaries::Rec709
>> + * \brief Rec.709 color primaries
>> + * \var ColorSpace::Primaries::Rec2020
>> + * \brief Rec.2020 color primaries
>> + */
>> +
>> +/**
>> + * \enum ColorSpace::YcbcrEncoding
>> + * \brief The Y'CbCr encoding
>> + *
>> + * \var ColorSpace::YcbcrEncoding::Undefined
>> + * \brief The Y'CbCr encoding is undefined
>> + * \var ColorSpace::YcbcrEncoding::Rec601
>> + * \brief Rec.601 Y'CbCr encoding
>> + * \var ColorSpace::YcbcrEncoding::Rec709
>> + * \brief Rec.709 Y'CbCr encoding
>> + * \var ColorSpace::YcbcrEncoding::Rec2020
>> + * \brief Rec.2020 Y'CbCr encoding
>> + */
>> +
>> +/**
>> + * \enum ColorSpace::TransferFunction
>> + * \brief The transfer function used for this color space
>> + *
>> + * \var ColorSpace::TransferFunction::Undefined
>> + * \brief The transfer function is not specified
>> + * \var ColorSpace::TransferFunction::Linear
>> + * \brief This color space uses a linear (identity) transfer function
>> + * \var ColorSpace::TransferFunction::Srgb
>> + * \brief sRGB transfer function
>> + * \var ColorSpace::TransferFunction::Rec709
>> + * \brief Rec709 transfer function
>> + */
>> +
>> +/**
>> + * \enum ColorSpace::Range
>> + * \brief The range (sometimes "quantisation") for this color space
>> + *
>> + * \var ColorSpace::Range::Undefined
>> + * \brief The range is not specified
>> + * \var ColorSpace::Range::Full
>> + * \brief This color space uses full range pixel values
>> + * \var ColorSpace::Range::Limited
>> + * \brief This color space uses limited range pixel values, being
>> + * 16 to 235 for Y' and 16 to 240 for Cb and Cr
>
>
> Perhaps add the range for 10-bit as well?

Good idea.

>
>>
>> + */
>> +
>> +/**
>> + * \fn ColorSpace::ColorSpace(Encoding e, TransferFunction t, Range r)
>> + * \brief Construct a ColorSpace from explicit values
>> + * \param[in] e The Y'CbCr encoding
>> + * \param[in] t The transfer function for the color space
>> + * \param[in] r The range of the pixel values in this color space
>> + */
>> +
>> +/**
>> + * \fn ColorSpace::ColorSpace()
>> + * \brief Construct a color space with undefined encoding, transfer function
>> + * and range
>> + */
>> +
>> +/**
>> + * \brief Check if all the fields of the color space are defined
>> + * \return Return true if all the fields of the color space are defined,
>> + * otherwise false
>> + */
>> +bool ColorSpace::isFullyDefined() const
>> +{
>> +       return primaries != Primaries::Undefined &&
>> +              ycbcrEncoding != YcbcrEncoding::Undefined &&
>> +              transferFunction != TransferFunction::Undefined &&
>> +              range != Range::Undefined;
>> +}
>> +
>> +/**
>> + * \brief Assemble and return a readable string representation of the
>> + * ColorSpace
>> + * \return A string describing the ColorSpace
>> + */
>> +const std::string ColorSpace::toString() const
>> +{
>> +       /* Print out a brief name only for standard color sapces. */
>> +
>> +       static const std::vector<std::pair<ColorSpace, const char *>> colorSpaceNames = {
>> +               { ColorSpace::Undefined, "Undefined" },
>> +               { ColorSpace::Raw, "Raw" },
>> +               { ColorSpace::Jpeg, "Jpeg" },
>> +               { 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;
>> +                              });
>
>
> Perhaps this might be better done with a std::map or even embedding the string
> into a member variable?  Not too fussed either way.

I've not not noticed other classes do that for debug strings, but It's
not a bad idea. What do other folks think?

I actually ran some little experiments with vectors vs. maps, just to
get some hints as to when it's worth the trouble of defining an
ordering (which you need for a map), or a hash (which you need for an
unordered_map). Running on a Pi I found:

* A std::map becomes faster than searching a vector of pairs after
about 20 or 30 entries.
* A std::unordered_map becomes faster than searching a vector at just
over 150 entries.
* A std::unordered_map requires "a few thousand" entries to become
faster than a std::map.

Thanks!

David

>
> Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
>
>>
>> +       if (it != colorSpaceNames.end())
>> +               return std::string(it->second);
>> +
>> +       static const char *primariesNames[] = {
>> +               "Undefined",
>> +               "Raw",
>> +               "Smpte170m",
>> +               "Rec709",
>> +               "Rec2020",
>> +       };
>> +       static const char *encodingNames[] = {
>> +               "Undefined",
>> +               "Rec601",
>> +               "Rec709",
>> +               "Rec2020",
>> +       };
>> +       static const char *transferFunctionNames[] = {
>> +               "Undefined",
>> +               "Linear",
>> +               "Srgb",
>> +               "Rec709",
>> +       };
>> +       static const char *rangeNames[] = {
>> +               "Undefined",
>> +               "Full",
>> +               "Limited",
>> +       };
>> +
>> +       std::stringstream ss;
>> +       ss << std::string(primariesNames[static_cast<int>(primaries)]) << "/"
>> +          << std::string(encodingNames[static_cast<int>(ycbcrEncoding)]) << "/"
>> +          << std::string(transferFunctionNames[static_cast<int>(transferFunction)]) << "/"
>> +          << std::string(rangeNames[static_cast<int>(range)]);
>> +
>> +       return ss.str();
>> +}
>> +
>> +/**
>> + * \var ColorSpace::primaries
>> + * \brief The color primaries
>> + */
>> +
>> +/**
>> + * \var ColorSpace::ycbcrEncoding
>> + * \brief The Y'CbCr encoding
>> + */
>> +
>> +/**
>> + * \var ColorSpace::transferFunction
>> + * \brief The transfer function for this color space
>> + */
>> +
>> +/**
>> + * \var ColorSpace::range
>> + * \brief The pixel range used by this color space
>> + */
>> +
>> +/**
>> + * \var ColorSpace::Undefined
>> + * \brief A constant representing a fully undefined color space
>> + */
>> +
>> +/**
>> + * \var ColorSpace::Raw
>> + * \brief A constant representing a raw color space (from a sensor)
>> + */
>> +
>> +/**
>> + * \var ColorSpace::Jpeg
>> + * \brief A constant representing the JPEG color space used for
>> + * encoding JPEG images (and regarded as being the same as the sRGB
>> + * color space)
>> + */
>> +
>> +/**
>> + * \var ColorSpace::Smpte170m
>> + * \brief A constant representing the SMPTE170M color space
>> + */
>> +
>> +/**
>> + * \var ColorSpace::Rec709
>> + * \brief A constant representing the Rec.709 color space
>> + */
>> +
>> +/**
>> + * \var ColorSpace::Rec2020
>> + * \brief A constant representing the Rec.2020 color space
>> + */
>> +
>> +/**
>> + * \brief Compare color spaces for equality
>> + * \return True if the two color spaces are identical, false otherwise
>> + */
>> +bool operator==(const ColorSpace &lhs, const ColorSpace &rhs)
>> +{
>> +       return lhs.primaries == rhs.primaries &&
>> +              lhs.ycbcrEncoding == rhs.ycbcrEncoding &&
>> +              lhs.transferFunction == rhs.transferFunction &&
>> +              lhs.range == rhs.range;
>> +}
>> +
>> +/**
>> + * \fn bool operator!=(const ColorSpace &lhs, const ColorSpace &rhs)
>> + * \brief Compare color spaces for inequality
>> + * \return True if the two color spaces are not identical, false otherwise
>> + */
>> +
>> +} /* namespace libcamera */
>> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
>> index 243dd3c1..8dc5d39d 100644
>> --- a/src/libcamera/meson.build
>> +++ b/src/libcamera/meson.build
>> @@ -8,6 +8,7 @@ libcamera_sources = files([
>>      'camera_manager.cpp',
>>      'camera_sensor.cpp',
>>      'camera_sensor_properties.cpp',
>> +    'color_space.cpp',
>>      'controls.cpp',
>>      'control_serializer.cpp',
>>      'control_validator.cpp',
>> --
>> 2.20.1
>>

Patch
diff mbox series

diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h
new file mode 100644
index 00000000..2af9da31
--- /dev/null
+++ b/include/libcamera/color_space.h
@@ -0,0 +1,88 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021, Raspberry Pi (Trading) Limited
+ *
+ * color_space.h - color space definitions
+ */
+
+#ifndef __LIBCAMERA_COLOR_SPACE_H__
+#define __LIBCAMERA_COLOR_SPACE_H__
+
+#include <string>
+
+namespace libcamera {
+
+class ColorSpace
+{
+public:
+	enum class Primaries : int {
+		Undefined,
+		Raw,
+		Smpte170m,
+		Rec709,
+		Rec2020,
+	};
+
+	enum class YcbcrEncoding : int {
+		Undefined,
+		Rec601,
+		Rec709,
+		Rec2020,
+	};
+
+	enum class TransferFunction : int {
+		Undefined,
+		Linear,
+		Srgb,
+		Rec709,
+	};
+
+	enum class Range : int {
+		Undefined,
+		Full,
+		Limited,
+	};
+
+	constexpr ColorSpace()
+		: ColorSpace(Primaries::Undefined, YcbcrEncoding::Undefined, TransferFunction::Undefined, Range::Undefined)
+	{
+	}
+
+	constexpr ColorSpace(Primaries p, YcbcrEncoding e, TransferFunction t, Range r)
+		: primaries(p), ycbcrEncoding(e), transferFunction(t), range(r)
+	{
+	}
+
+	static const ColorSpace Undefined;
+	static const ColorSpace Raw;
+	static const ColorSpace Jpeg;
+	static const ColorSpace Smpte170m;
+	static const ColorSpace Rec709;
+	static const ColorSpace Rec2020;
+
+	Primaries primaries;
+	YcbcrEncoding ycbcrEncoding;
+	TransferFunction transferFunction;
+	Range range;
+
+	bool isFullyDefined() const;
+
+	const std::string toString() const;
+};
+
+constexpr ColorSpace ColorSpace::Undefined = { Primaries::Undefined, YcbcrEncoding::Undefined, TransferFunction::Undefined, Range::Undefined };
+constexpr ColorSpace ColorSpace::Raw = { Primaries::Raw, YcbcrEncoding::Rec601, TransferFunction::Linear, Range::Full };
+constexpr ColorSpace ColorSpace::Jpeg = { Primaries::Rec709, YcbcrEncoding::Rec601, TransferFunction::Srgb, Range::Full };
+constexpr ColorSpace ColorSpace::Smpte170m = { Primaries::Smpte170m, YcbcrEncoding::Rec601, TransferFunction::Rec709, Range::Limited };
+constexpr ColorSpace ColorSpace::Rec709 = { Primaries::Rec709, YcbcrEncoding::Rec709, TransferFunction::Rec709, Range::Limited };
+constexpr ColorSpace ColorSpace::Rec2020 = { Primaries::Rec2020, YcbcrEncoding::Rec2020, TransferFunction::Rec709, Range::Limited };
+
+bool operator==(const ColorSpace &lhs, const ColorSpace &rhs);
+static inline bool operator!=(const ColorSpace &lhs, const ColorSpace &rhs)
+{
+	return !(lhs == rhs);
+}
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_COLOR_SPACE_H__ */
diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build
index 7155ff20..131e1740 100644
--- a/include/libcamera/meson.build
+++ b/include/libcamera/meson.build
@@ -5,6 +5,7 @@  libcamera_include_dir = 'libcamera' / 'libcamera'
 libcamera_public_headers = files([
     'camera.h',
     'camera_manager.h',
+    'color_space.h',
     'compiler.h',
     'controls.h',
     'file_descriptor.h',
diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp
new file mode 100644
index 00000000..aec4107f
--- /dev/null
+++ b/src/libcamera/color_space.cpp
@@ -0,0 +1,256 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021, Raspberry Pi (Trading) Limited
+ *
+ * color_space.cpp - color spaces.
+ */
+
+#include <libcamera/color_space.h>
+
+#include <algorithm>
+#include <sstream>
+#include <vector>
+
+/**
+ * \file color_space.h
+ * \brief Class and enums to represent color spaces
+ */
+
+namespace libcamera {
+
+/**
+ * \class ColorSpace
+ * \brief Class to describe a color space
+ *
+ * The ColorSpace class defines the color primaries, the Y'CbCr encoding,
+ * the transfer function associated with the color space, and the range
+ * (sometimes also referred to as the quantisation) of the color space.
+ *
+ * Certain combinations of these fields form well-known standard color
+ * spaces such as "JPEG" or "REC709". Applications must not request color
+ * spaces with undefined fields, but the "Undefined" value may be
+ * returned if the camera drivers decide to use a color space that is
+ * not recognised by the ColorSpace class.
+ *
+ * For more information on the specific color spaces described here, please
+ * see:
+ *
+ * <a href="https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-srgb">sRGB</a> and <a href="https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-jpeg">JPEG</a>
+>* <a href="https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-smpte-170m">SMPTE 170M</a>
+ * <a href="https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-rec709">Rec.709</a>
+ * <a href="https://www.kernel.org/doc/html/v4.8/media/uapi/v4l/pixfmt-007.html#col-bt2020">Rec.2020</a>
+ */
+
+/**
+ * \enum ColorSpace::Primaries
+ * \brief The color primaries for this color space
+ *
+ * \var ColorSpace::Primaries::Undefined
+ * \brief The color primaries are undefined
+ * \var ColorSpace::Primaries::Raw
+ * \brief These are raw colors directly from a sensor
+ * \var ColorSpace::Primaries::Smpte170m
+ * \brief SMPTE 170M color primaries
+ * \var ColorSpace::Primaries::Rec709
+ * \brief Rec.709 color primaries
+ * \var ColorSpace::Primaries::Rec2020
+ * \brief Rec.2020 color primaries
+ */
+
+/**
+ * \enum ColorSpace::YcbcrEncoding
+ * \brief The Y'CbCr encoding
+ *
+ * \var ColorSpace::YcbcrEncoding::Undefined
+ * \brief The Y'CbCr encoding is undefined
+ * \var ColorSpace::YcbcrEncoding::Rec601
+ * \brief Rec.601 Y'CbCr encoding
+ * \var ColorSpace::YcbcrEncoding::Rec709
+ * \brief Rec.709 Y'CbCr encoding
+ * \var ColorSpace::YcbcrEncoding::Rec2020
+ * \brief Rec.2020 Y'CbCr encoding
+ */
+
+/**
+ * \enum ColorSpace::TransferFunction
+ * \brief The transfer function used for this color space
+ *
+ * \var ColorSpace::TransferFunction::Undefined
+ * \brief The transfer function is not specified
+ * \var ColorSpace::TransferFunction::Linear
+ * \brief This color space uses a linear (identity) transfer function
+ * \var ColorSpace::TransferFunction::Srgb
+ * \brief sRGB transfer function
+ * \var ColorSpace::TransferFunction::Rec709
+ * \brief Rec709 transfer function
+ */
+
+/**
+ * \enum ColorSpace::Range
+ * \brief The range (sometimes "quantisation") for this color space
+ *
+ * \var ColorSpace::Range::Undefined
+ * \brief The range is not specified
+ * \var ColorSpace::Range::Full
+ * \brief This color space uses full range pixel values
+ * \var ColorSpace::Range::Limited
+ * \brief This color space uses limited range pixel values, being
+ * 16 to 235 for Y' and 16 to 240 for Cb and Cr
+ */
+
+/**
+ * \fn ColorSpace::ColorSpace(Encoding e, TransferFunction t, Range r)
+ * \brief Construct a ColorSpace from explicit values
+ * \param[in] e The Y'CbCr encoding
+ * \param[in] t The transfer function for the color space
+ * \param[in] r The range of the pixel values in this color space
+ */
+
+/**
+ * \fn ColorSpace::ColorSpace()
+ * \brief Construct a color space with undefined encoding, transfer function
+ * and range
+ */
+
+/**
+ * \brief Check if all the fields of the color space are defined
+ * \return Return true if all the fields of the color space are defined,
+ * otherwise false
+ */
+bool ColorSpace::isFullyDefined() const
+{
+	return primaries != Primaries::Undefined &&
+	       ycbcrEncoding != YcbcrEncoding::Undefined &&
+	       transferFunction != TransferFunction::Undefined &&
+	       range != Range::Undefined;
+}
+
+/**
+ * \brief Assemble and return a readable string representation of the
+ * ColorSpace
+ * \return A string describing the ColorSpace
+ */
+const std::string ColorSpace::toString() const
+{
+	/* Print out a brief name only for standard color sapces. */
+
+	static const std::vector<std::pair<ColorSpace, const char *>> colorSpaceNames = {
+		{ ColorSpace::Undefined, "Undefined" },
+		{ ColorSpace::Raw, "Raw" },
+		{ ColorSpace::Jpeg, "Jpeg" },
+		{ 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;
+			       });
+	if (it != colorSpaceNames.end())
+		return std::string(it->second);
+
+	static const char *primariesNames[] = {
+		"Undefined",
+		"Raw",
+		"Smpte170m",
+		"Rec709",
+		"Rec2020",
+	};
+	static const char *encodingNames[] = {
+		"Undefined",
+		"Rec601",
+		"Rec709",
+		"Rec2020",
+	};
+	static const char *transferFunctionNames[] = {
+		"Undefined",
+		"Linear",
+		"Srgb",
+		"Rec709",
+	};
+	static const char *rangeNames[] = {
+		"Undefined",
+		"Full",
+		"Limited",
+	};
+
+	std::stringstream ss;
+	ss << std::string(primariesNames[static_cast<int>(primaries)]) << "/"
+	   << std::string(encodingNames[static_cast<int>(ycbcrEncoding)]) << "/"
+	   << std::string(transferFunctionNames[static_cast<int>(transferFunction)]) << "/"
+	   << std::string(rangeNames[static_cast<int>(range)]);
+
+	return ss.str();
+}
+
+/**
+ * \var ColorSpace::primaries
+ * \brief The color primaries
+ */
+
+/**
+ * \var ColorSpace::ycbcrEncoding
+ * \brief The Y'CbCr encoding
+ */
+
+/**
+ * \var ColorSpace::transferFunction
+ * \brief The transfer function for this color space
+ */
+
+/**
+ * \var ColorSpace::range
+ * \brief The pixel range used by this color space
+ */
+
+/**
+ * \var ColorSpace::Undefined
+ * \brief A constant representing a fully undefined color space
+ */
+
+/**
+ * \var ColorSpace::Raw
+ * \brief A constant representing a raw color space (from a sensor)
+ */
+
+/**
+ * \var ColorSpace::Jpeg
+ * \brief A constant representing the JPEG color space used for
+ * encoding JPEG images (and regarded as being the same as the sRGB
+ * color space)
+ */
+
+/**
+ * \var ColorSpace::Smpte170m
+ * \brief A constant representing the SMPTE170M color space
+ */
+
+/**
+ * \var ColorSpace::Rec709
+ * \brief A constant representing the Rec.709 color space
+ */
+
+/**
+ * \var ColorSpace::Rec2020
+ * \brief A constant representing the Rec.2020 color space
+ */
+
+/**
+ * \brief Compare color spaces for equality
+ * \return True if the two color spaces are identical, false otherwise
+ */
+bool operator==(const ColorSpace &lhs, const ColorSpace &rhs)
+{
+	return lhs.primaries == rhs.primaries &&
+	       lhs.ycbcrEncoding == rhs.ycbcrEncoding &&
+	       lhs.transferFunction == rhs.transferFunction &&
+	       lhs.range == rhs.range;
+}
+
+/**
+ * \fn bool operator!=(const ColorSpace &lhs, const ColorSpace &rhs)
+ * \brief Compare color spaces for inequality
+ * \return True if the two color spaces are not identical, false otherwise
+ */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index 243dd3c1..8dc5d39d 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -8,6 +8,7 @@  libcamera_sources = files([
     'camera_manager.cpp',
     'camera_sensor.cpp',
     'camera_sensor_properties.cpp',
+    'color_space.cpp',
     'controls.cpp',
     'control_serializer.cpp',
     'control_validator.cpp',