[{"id":24777,"web_url":"https://patchwork.libcamera.org/comment/24777/","msgid":"<20220825190645.GO109174@pyrite.rasen.tech>","date":"2022-08-25T19:06:45","subject":"Re: [libcamera-devel] [PATCH v1.1 3/6] libcamera: color_space: Add\n\tfromString() function","submitter":{"id":97,"url":"https://patchwork.libcamera.org/api/people/97/","name":"Nicolas Dufresne via libcamera-devel","email":"libcamera-devel@lists.libcamera.org"},"content":"On Thu, Aug 25, 2022 at 12:34:14AM +0300, Laurent Pinchart via libcamera-devel wrote:\n> Add a ColorSpace:fromString() function to parse a string into a color\n> space. The string can either contain the name of a well-known color\n> space, or four color space components separate by a '/' character.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>\n\nReviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n\n> ---\n> Changes since v1:\n> \n> - Document the format of the color space string\n> ---\n>  include/libcamera/color_space.h |   2 +\n>  src/libcamera/color_space.cpp   | 153 +++++++++++++++++++++++++-------\n>  2 files changed, 125 insertions(+), 30 deletions(-)\n> \n> diff --git a/include/libcamera/color_space.h b/include/libcamera/color_space.h\n> index 8030a264c66f..f493f72d2db8 100644\n> --- a/include/libcamera/color_space.h\n> +++ b/include/libcamera/color_space.h\n> @@ -59,6 +59,8 @@ public:\n>  \n>  \tstd::string toString() const;\n>  \tstatic std::string toString(const std::optional<ColorSpace> &colorSpace);\n> +\n> +\tstatic std::optional<ColorSpace> fromString(const std::string &str);\n>  };\n>  \n>  bool operator==(const ColorSpace &lhs, const ColorSpace &rhs);\n> diff --git a/src/libcamera/color_space.cpp b/src/libcamera/color_space.cpp\n> index 1b2dd2404452..944d0e98099b 100644\n> --- a/src/libcamera/color_space.cpp\n> +++ b/src/libcamera/color_space.cpp\n> @@ -12,6 +12,9 @@\n>  #include <map>\n>  #include <sstream>\n>  #include <utility>\n> +#include <vector>\n> +\n> +#include <libcamera/base/utils.h>\n>  \n>  /**\n>   * \\file color_space.h\n> @@ -208,6 +211,44 @@ const ColorSpace ColorSpace::Rec2020 = {\n>   * \\brief The pixel range used with by color space\n>   */\n>  \n> +namespace {\n> +\n> +const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {\n> +\t{ ColorSpace::Raw, \"RAW\" },\n> +\t{ ColorSpace::Srgb, \"sRGB\" },\n> +\t{ ColorSpace::Sycc, \"sYCC\" },\n> +\t{ ColorSpace::Smpte170m, \"SMPTE170M\" },\n> +\t{ ColorSpace::Rec709, \"Rec709\" },\n> +\t{ ColorSpace::Rec2020, \"Rec2020\" },\n> +} };\n> +\n> +const std::map<ColorSpace::Primaries, std::string> primariesNames = {\n> +\t{ ColorSpace::Primaries::Raw, \"RAW\" },\n> +\t{ ColorSpace::Primaries::Smpte170m, \"SMPTE170M\" },\n> +\t{ ColorSpace::Primaries::Rec709, \"Rec709\" },\n> +\t{ ColorSpace::Primaries::Rec2020, \"Rec2020\" },\n> +};\n> +\n> +const std::map<ColorSpace::TransferFunction, std::string> transferNames = {\n> +\t{ ColorSpace::TransferFunction::Linear, \"Linear\" },\n> +\t{ ColorSpace::TransferFunction::Srgb, \"sRGB\" },\n> +\t{ ColorSpace::TransferFunction::Rec709, \"Rec709\" },\n> +};\n> +\n> +const std::map<ColorSpace::YcbcrEncoding, std::string> encodingNames = {\n> +\t{ ColorSpace::YcbcrEncoding::None, \"None\" },\n> +\t{ ColorSpace::YcbcrEncoding::Rec601, \"Rec601\" },\n> +\t{ ColorSpace::YcbcrEncoding::Rec709, \"Rec709\" },\n> +\t{ ColorSpace::YcbcrEncoding::Rec2020, \"Rec2020\" },\n> +};\n> +\n> +const std::map<ColorSpace::Range, std::string> rangeNames = {\n> +\t{ ColorSpace::Range::Full, \"Full\" },\n> +\t{ ColorSpace::Range::Limited, \"Limited\" },\n> +};\n> +\n> +} /* namespace */\n> +\n>  /**\n>   * \\brief Assemble and return a readable string representation of the\n>   * ColorSpace\n> @@ -223,14 +264,6 @@ std::string ColorSpace::toString() const\n>  {\n>  \t/* Print out a brief name only for standard color spaces. */\n>  \n> -\tstatic const std::array<std::pair<ColorSpace, const char *>, 6> colorSpaceNames = { {\n> -\t\t{ ColorSpace::Raw, \"RAW\" },\n> -\t\t{ ColorSpace::Srgb, \"sRGB\" },\n> -\t\t{ ColorSpace::Sycc, \"sYCC\" },\n> -\t\t{ ColorSpace::Smpte170m, \"SMPTE170M\" },\n> -\t\t{ ColorSpace::Rec709, \"Rec709\" },\n> -\t\t{ ColorSpace::Rec2020, \"Rec2020\" },\n> -\t} };\n>  \tauto it = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),\n>  \t\t\t       [this](const auto &item) {\n>  \t\t\t\t       return *this == item.first;\n> @@ -240,28 +273,6 @@ std::string ColorSpace::toString() const\n>  \n>  \t/* Assemble a name made of the constituent fields. */\n>  \n> -\tstatic const std::map<Primaries, std::string> primariesNames = {\n> -\t\t{ Primaries::Raw, \"RAW\" },\n> -\t\t{ Primaries::Smpte170m, \"SMPTE170M\" },\n> -\t\t{ Primaries::Rec709, \"Rec709\" },\n> -\t\t{ Primaries::Rec2020, \"Rec2020\" },\n> -\t};\n> -\tstatic const std::map<TransferFunction, std::string> transferNames = {\n> -\t\t{ TransferFunction::Linear, \"Linear\" },\n> -\t\t{ TransferFunction::Srgb, \"sRGB\" },\n> -\t\t{ TransferFunction::Rec709, \"Rec709\" },\n> -\t};\n> -\tstatic const std::map<YcbcrEncoding, std::string> encodingNames = {\n> -\t\t{ YcbcrEncoding::None, \"None\" },\n> -\t\t{ YcbcrEncoding::Rec601, \"Rec601\" },\n> -\t\t{ YcbcrEncoding::Rec709, \"Rec709\" },\n> -\t\t{ YcbcrEncoding::Rec2020, \"Rec2020\" },\n> -\t};\n> -\tstatic const std::map<Range, std::string> rangeNames = {\n> -\t\t{ Range::Full, \"Full\" },\n> -\t\t{ Range::Limited, \"Limited\" },\n> -\t};\n> -\n>  \tauto itPrimaries = primariesNames.find(primaries);\n>  \tstd::string primariesName =\n>  \t\titPrimaries == primariesNames.end() ? \"Invalid\" : itPrimaries->second;\n> @@ -303,6 +314,88 @@ std::string ColorSpace::toString(const std::optional<ColorSpace> &colorSpace)\n>  \treturn colorSpace->toString();\n>  }\n>  \n> +/**\n> + * \\brief Construct a color space from a string\n> + * \\param[in] str The string\n> + *\n> + * The string \\a str can contain the name of a well-known color space, or be\n> + * made of the four color space components separated by a '/' character, ordered\n> + * as\n> + *\n> + * \\verbatim primaries '/' transferFunction '/' ycbcrEncoding '/' range \\endverbatim\n> + *\n> + * Any failure to parse the string, either because it doesn't match the expected\n> + * format, or because the one of the names isn't recognized, will cause this\n> + * function to return std::nullopt.\n> + *\n> + * \\return The ColorSpace corresponding to the string, or std::nullopt if the\n> + * string doesn't describe a known color space\n> + */\n> +std::optional<ColorSpace> ColorSpace::fromString(const std::string &str)\n> +{\n> +\t/* First search for a standard color space name match. */\n> +\tauto itColorSpace = std::find_if(colorSpaceNames.begin(), colorSpaceNames.end(),\n> +\t\t\t\t\t [&str](const auto &item) {\n> +\t\t\t\t\t\t return str == item.second;\n> +\t\t\t\t\t });\n> +\tif (itColorSpace != colorSpaceNames.end())\n> +\t\treturn itColorSpace->first;\n> +\n> +\t/*\n> +\t * If not found, the string must contain the four color space\n> +\t * components separated by a '/' character.\n> +\t */\n> +\tconst auto &split = utils::split(str, \"/\");\n> +\tstd::vector<std::string> components{ split.begin(), split.end() };\n> +\n> +\tif (components.size() != 4)\n> +\t\treturn std::nullopt;\n> +\n> +\tColorSpace colorSpace = ColorSpace::Raw;\n> +\n> +\t/* Color primaries */\n> +\tauto itPrimaries = std::find_if(primariesNames.begin(), primariesNames.end(),\n> +\t\t\t\t\t[&components](const auto &item) {\n> +\t\t\t\t\t\treturn components[0] == item.second;\n> +\t\t\t\t\t});\n> +\tif (itPrimaries == primariesNames.end())\n> +\t\treturn std::nullopt;\n> +\n> +\tcolorSpace.primaries = itPrimaries->first;\n> +\n> +\t/* Transfer function */\n> +\tauto itTransfer = std::find_if(transferNames.begin(), transferNames.end(),\n> +\t\t\t\t       [&components](const auto &item) {\n> +\t\t\t\t\t       return components[1] == item.second;\n> +\t\t\t\t       });\n> +\tif (itTransfer == transferNames.end())\n> +\t\treturn std::nullopt;\n> +\n> +\tcolorSpace.transferFunction = itTransfer->first;\n> +\n> +\t/* YCbCr encoding */\n> +\tauto itEncoding = std::find_if(encodingNames.begin(), encodingNames.end(),\n> +\t\t\t\t       [&components](const auto &item) {\n> +\t\t\t\t\t       return components[2] == item.second;\n> +\t\t\t\t       });\n> +\tif (itEncoding == encodingNames.end())\n> +\t\treturn std::nullopt;\n> +\n> +\tcolorSpace.ycbcrEncoding = itEncoding->first;\n> +\n> +\t/* Quantization range */\n> +\tauto itRange = std::find_if(rangeNames.begin(), rangeNames.end(),\n> +\t\t\t\t    [&components](const auto &item) {\n> +\t\t\t\t\t    return components[3] == item.second;\n> +\t\t\t\t    });\n> +\tif (itRange == rangeNames.end())\n> +\t\treturn std::nullopt;\n> +\n> +\tcolorSpace.range = itRange->first;\n> +\n> +\treturn colorSpace;\n> +}\n> +\n>  /**\n>   * \\brief Compare color spaces for equality\n>   * \\return True if the two color spaces are identical, false otherwise","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 03021C0DA4\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 25 Aug 2022 19:06:56 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5497461FBF;\n\tThu, 25 Aug 2022 21:06:55 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2846A61FA0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 25 Aug 2022 21:06:53 +0200 (CEST)","from pyrite.rasen.tech (unknown\n\t[IPv6:2604:2d80:ad8a:9000:1bf9:855b:22de:3645])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 0BE402B3;\n\tThu, 25 Aug 2022 21:06:51 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1661454415;\n\tbh=5or1hvu5jwBP+0U2l8kGMzDYrrEn3l2E1CoxP4HLXW4=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=3QP1zCDsql0cPxr/F5F3bUQE+tpMutxrLDUacgvuk/Loxz81RMo8fRBgd6TLVdxk/\n\tUXy9I8JpQm8rfsjlR89rGyFg0NDMnbUhlS8FkWqEKxcHSau5B9xaqexJxqzYuJ9HHf\n\tOJvP2PWlVXL/A0Hab/BgTOBqHYI8vkdmBqKJt7UiMDrAZBbc92AA2S7VIJ6XloGgPO\n\tI+iNB9f7lM0+2pQPgSYrqwOCx4VtWVlmeO9NgopWtlsEvdnnLAU1cDJBcS44+9CXdI\n\toeH2Mqj5085bFre9ZtQsZ85QG1GyeihIk+Jay0YgzX6823z3oRlDMYL5rS2k1/PcGb\n\tU5rvNo7jwQ0ng==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1661454412;\n\tbh=5or1hvu5jwBP+0U2l8kGMzDYrrEn3l2E1CoxP4HLXW4=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=m0apV5E1MxtJPKBD1FYwOs2IhAXdKcKurema8Jiz2xtuaXhltlyoTFCPeEYG06vAZ\n\tnKYCWyqB1MsnRoqmHOfkRIrCP0q1VBH+0RqXBTUgaDjIluH9OxQ0nB7RLBhSXSKRmy\n\tPaBLaSO49V7o8wLJiYjlPHfm1L8omNCpkIH/KzKo="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"m0apV5E1\"; dkim-atps=neutral","Date":"Thu, 25 Aug 2022 14:06:45 -0500","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Message-ID":"<20220825190645.GO109174@pyrite.rasen.tech>","References":"<20220823174314.14881-4-laurent.pinchart@ideasonboard.com>\n\t<20220824213414.24164-1-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=us-ascii","Content-Disposition":"inline","In-Reply-To":"<20220824213414.24164-1-laurent.pinchart@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v1.1 3/6] libcamera: color_space: Add\n\tfromString() function","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Paul Elder via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"paul.elder@ideasonboard.com","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]