blob: afcaabc519f73c0fc2ea7e2239a9bf594d281878 [file] [log] [blame]
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* formats.cpp - libcamera image formats
*/
#include "libcamera/internal/formats.h"
#include <algorithm>
#include <errno.h>
#include <libcamera/base/log.h>
#include <libcamera/base/utils.h>
#include <libcamera/formats.h>
/**
* \file internal/formats.h
* \brief Types and helper functions to handle libcamera image formats
*/
namespace libcamera {
LOG_DEFINE_CATEGORY(Formats)
/**
* \class PixelFormatInfo
* \brief Information about pixel formats
*
* The PixelFormatInfo class groups together information describing a pixel
* format. It facilitates handling of pixel formats by providing data commonly
* used in pipeline handlers.
*
* \var PixelFormatInfo::name
* \brief The format name as a human-readable string, used as the test
* representation of the PixelFormat
*
* \var PixelFormatInfo::format
* \brief The PixelFormat described by this instance
*
* \var PixelFormatInfo::v4l2Formats
* \brief The V4L2 pixel formats corresponding to the PixelFormat
*
* Multiple V4L2 formats may exist for one PixelFormat when the format uses
* multiple planes, as V4L2 defines separate 4CCs for contiguous and separate
* planes formats. The two entries in the array store the contiguous and
* non-contiguous V4L2 formats respectively. If the PixelFormat isn't a
* multiplanar format, or if no corresponding non-contiguous V4L2 format
* exists, the second entry is invalid.
*
* \var PixelFormatInfo::bitsPerPixel
* \brief The average number of bits per pixel
*
* The number per pixel averages the total number of bits for all colour
* components over the whole image, excluding any padding bits or padding
* pixels.
*
* For formats that store pixels with bit padding within words, only the
* effective bits are taken into account. For instance, 12-bit Bayer data
* stored in two bytes per pixel report 12, not 16, in this field.
*
* Formats that don't have a fixed number of bits per pixel, such as compressed
* formats, report 0 in this field.
*
* \var PixelFormatInfo::colourEncoding
* \brief The colour encoding type
*
* \var PixelFormatInfo::packed
* \brief Tell if multiple pixels are packed in the same bytes
*
* Packed formats are defined as storing data from multiple pixels in the same
* bytes. For instance, 12-bit Bayer data with two pixels stored in three bytes
* is packed, while the same data stored with 4 bits of padding in two bytes
* per pixel is not packed.
*
* \var PixelFormatInfo::pixelsPerGroup
* \brief The number of pixels in a pixel group
*
* A pixel group is defined as the minimum number of pixels (including padding)
* necessary in a row when the image has only one column of effective pixels.
* pixelsPerGroup refers to this value. PixelFormatInfo::Plane::bytesPerGroup,
* then, refers to the number of bytes that a pixel group consumes. This
* definition of a pixel group allows simple calculation of stride, as
* ceil(width / pixelsPerGroup) * bytesPerGroup. These values are determined
* only in terms of a row. The ceiling accounts for padding.
*
* A pixel group has a second constraint, such that the pixel group
* (bytesPerGroup and pixelsPerGroup) is the smallest repeatable unit.
* What this means is that, for example, in the IPU3 formats, if there is only
* one column of effective pixels, it looks like it could be fit in 5 bytes
* with 3 padding pixels (for a total of 4 pixels over 5 bytes). However, this
* unit is not repeatable, as at the 7th group in the same row, the pattern
* is broken. Therefore, the pixel group for IPU3 formats must be 25 pixels
* over 32 bytes.
*
* For example, for something simple like BGR888, it is self-explanatory:
* the pixel group size is 1, and the bytes necessary is 3, and there is
* only one plane with no (= 1) vertical subsampling. For YUYV, the
* CbCr pair is shared between two pixels, so even if you have only one
* pixel, you would still need a padded second Y sample, therefore the pixel
* group size is 2, and bytes necessary is 4. YUYV also has no vertical
* subsampling. NV12 has a pixel group size of 2 pixels, due to the CbCr plane.
* The bytes per group then, for both planes, is 2. The first plane has no
* vertical subsampling, but the second plane is subsampled by a factor of 2.
*
* The IPU3 raw Bayer formats are single-planar, and have a pixel group size of
* 25, consuming 32 bytes, due to the packing pattern being repeated in memory
* every 32 bytes. The IPU3 hardware, however, has an additional constraint on
* the DMA burst size, requiring lines to be multiple of 64 bytes. This isn't an
* intrinsic property of the formats and is thus not reflected here. It is
* instead enforced by the corresponding pipeline handler.
*
* \var PixelFormatInfo::planes
* \brief Information about pixels for each plane
*
* \sa PixelFormatInfo::Plane
*/
/**
* \enum PixelFormatInfo::ColourEncoding
* \brief The colour encoding type
*
* \var PixelFormatInfo::ColourEncodingRGB
* \brief RGB colour encoding
*
* \var PixelFormatInfo::ColourEncodingYUV
* \brief YUV colour encoding
*
* \var PixelFormatInfo::ColourEncodingRAW
* \brief RAW colour encoding
*/
/**
* \struct PixelFormatInfo::Plane
* \brief Information about a single plane of a pixel format
*
* \var PixelFormatInfo::Plane::bytesPerGroup
* \brief The number of bytes that a pixel group consumes
*
* \sa PixelFormatInfo::pixelsPerGroup
*
* \var PixelFormatInfo::Plane::verticalSubSampling
* \brief Vertical subsampling multiplier
*
* This value is the ratio between the number of rows of pixels in the frame
* to the number of rows of pixels in the plane.
*/
namespace {
const PixelFormatInfo pixelFormatInfoInvalid{};
const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
/* RGB formats. */
{ formats::RGB565, {
.name = "RGB565",
.format = formats::RGB565,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_RGB565),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 16,
.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::RGB565_BE, {
.name = "RGB565_BE",
.format = formats::RGB565_BE,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_RGB565X),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 16,
.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::BGR888, {
.name = "BGR888",
.format = formats::BGR888,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_RGB24),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 24,
.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::RGB888, {
.name = "RGB888",
.format = formats::RGB888,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_BGR24),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 24,
.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::XRGB8888, {
.name = "XRGB8888",
.format = formats::XRGB8888,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_XBGR32),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 32,
.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::XBGR8888, {
.name = "XBGR8888",
.format = formats::XBGR8888,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_RGBX32),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 32,
.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::BGRX8888, {
.name = "BGRX8888",
.format = formats::BGRX8888,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_XRGB32),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 32,
.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::ABGR8888, {
.name = "ABGR8888",
.format = formats::ABGR8888,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_RGBA32),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 32,
.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::ARGB8888, {
.name = "ARGB8888",
.format = formats::ARGB8888,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_ABGR32),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 32,
.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::BGRA8888, {
.name = "BGRA8888",
.format = formats::BGRA8888,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_ARGB32),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 32,
.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::RGBA8888, {
.name = "RGBA8888",
.format = formats::RGBA8888,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_BGRA32),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 32,
.colourEncoding = PixelFormatInfo::ColourEncodingRGB,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
/* YUV packed formats. */
{ formats::YUYV, {
.name = "YUYV",
.format = formats::YUYV,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_YUYV),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 16,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::YVYU, {
.name = "YVYU",
.format = formats::YVYU,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_YVYU),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 16,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::UYVY, {
.name = "UYVY",
.format = formats::UYVY,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_UYVY),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 16,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::VYUY, {
.name = "VYUY",
.format = formats::VYUY,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_VYUY),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 16,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
/* YUV planar formats. */
{ formats::NV12, {
.name = "NV12",
.format = formats::NV12,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_NV12),
.multi = V4L2PixelFormat(V4L2_PIX_FMT_NV12M),
},
.bitsPerPixel = 12,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 2, 1 }, { 2, 2 }, { 0, 0 } }},
} },
{ formats::NV21, {
.name = "NV21",
.format = formats::NV21,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_NV21),
.multi = V4L2PixelFormat(V4L2_PIX_FMT_NV21M),
},
.bitsPerPixel = 12,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 2, 1 }, { 2, 2 }, { 0, 0 } }},
} },
{ formats::NV16, {
.name = "NV16",
.format = formats::NV16,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_NV16),
.multi = V4L2PixelFormat(V4L2_PIX_FMT_NV16M),
},
.bitsPerPixel = 16,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 2, 1 }, { 2, 1 }, { 0, 0 } }},
} },
{ formats::NV61, {
.name = "NV61",
.format = formats::NV61,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_NV61),
.multi = V4L2PixelFormat(V4L2_PIX_FMT_NV61M),
},
.bitsPerPixel = 16,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 2, 1 }, { 2, 1 }, { 0, 0 } }},
} },
{ formats::NV24, {
.name = "NV24",
.format = formats::NV24,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_NV24),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 24,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 1, 1 }, { 2, 1 }, { 0, 0 } }},
} },
{ formats::NV42, {
.name = "NV42",
.format = formats::NV42,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_NV42),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 24,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 1, 1 }, { 2, 1 }, { 0, 0 } }},
} },
{ formats::YUV420, {
.name = "YUV420",
.format = formats::YUV420,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_YUV420),
.multi = V4L2PixelFormat(V4L2_PIX_FMT_YUV420M),
},
.bitsPerPixel = 12,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 2, 1 }, { 1, 2 }, { 1, 2 } }},
} },
{ formats::YVU420, {
.name = "YVU420",
.format = formats::YVU420,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_YVU420),
.multi = V4L2PixelFormat(V4L2_PIX_FMT_YVU420M),
},
.bitsPerPixel = 12,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 2, 1 }, { 1, 2 }, { 1, 2 } }},
} },
{ formats::YUV422, {
.name = "YUV422",
.format = formats::YUV422,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_YUV422P),
.multi = V4L2PixelFormat(V4L2_PIX_FMT_YUV422M),
},
.bitsPerPixel = 16,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 2, 1 }, { 1, 1 }, { 1, 1 } }},
} },
/* Greyscale formats. */
{ formats::R8, {
.name = "R8",
.format = formats::R8,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_GREY),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 8,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 1, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::R10, {
.name = "R10",
.format = formats::R10,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_Y10),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 10,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::R12, {
.name = "R12",
.format = formats::R12,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_Y12),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 12,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::R10_CSI2P, {
.name = "R10_CSI2P",
.format = formats::R10,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_Y10P),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 10,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = true,
.pixelsPerGroup = 4,
.planes = {{ { 5, 1 }, { 0, 0 }, { 0, 0 } }},
} },
/* Bayer formats. */
{ formats::SBGGR8, {
.name = "SBGGR8",
.format = formats::SBGGR8,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR8),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 8,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SGBRG8, {
.name = "SGBRG8",
.format = formats::SGBRG8,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG8),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 8,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SGRBG8, {
.name = "SGRBG8",
.format = formats::SGRBG8,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG8),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 8,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SRGGB8, {
.name = "SRGGB8",
.format = formats::SRGGB8,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB8),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 8,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 2, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SBGGR10, {
.name = "SBGGR10",
.format = formats::SBGGR10,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 10,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SGBRG10, {
.name = "SGBRG10",
.format = formats::SGBRG10,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 10,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SGRBG10, {
.name = "SGRBG10",
.format = formats::SGRBG10,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 10,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SRGGB10, {
.name = "SRGGB10",
.format = formats::SRGGB10,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 10,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SBGGR10_CSI2P, {
.name = "SBGGR10_CSI2P",
.format = formats::SBGGR10_CSI2P,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 10,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = true,
.pixelsPerGroup = 4,
.planes = {{ { 5, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SGBRG10_CSI2P, {
.name = "SGBRG10_CSI2P",
.format = formats::SGBRG10_CSI2P,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 10,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = true,
.pixelsPerGroup = 4,
.planes = {{ { 5, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SGRBG10_CSI2P, {
.name = "SGRBG10_CSI2P",
.format = formats::SGRBG10_CSI2P,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 10,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = true,
.pixelsPerGroup = 4,
.planes = {{ { 5, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SRGGB10_CSI2P, {
.name = "SRGGB10_CSI2P",
.format = formats::SRGGB10_CSI2P,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 10,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = true,
.pixelsPerGroup = 4,
.planes = {{ { 5, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SBGGR12, {
.name = "SBGGR12",
.format = formats::SBGGR12,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 12,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SGBRG12, {
.name = "SGBRG12",
.format = formats::SGBRG12,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 12,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SGRBG12, {
.name = "SGRBG12",
.format = formats::SGRBG12,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 12,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SRGGB12, {
.name = "SRGGB12",
.format = formats::SRGGB12,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 12,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SBGGR12_CSI2P, {
.name = "SBGGR12_CSI2P",
.format = formats::SBGGR12_CSI2P,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12P),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 12,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = true,
.pixelsPerGroup = 2,
.planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SGBRG12_CSI2P, {
.name = "SGBRG12_CSI2P",
.format = formats::SGBRG12_CSI2P,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12P),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 12,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = true,
.pixelsPerGroup = 2,
.planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SGRBG12_CSI2P, {
.name = "SGRBG12_CSI2P",
.format = formats::SGRBG12_CSI2P,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG12P),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 12,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = true,
.pixelsPerGroup = 2,
.planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SRGGB12_CSI2P, {
.name = "SRGGB12_CSI2P",
.format = formats::SRGGB12_CSI2P,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB12P),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 12,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = true,
.pixelsPerGroup = 2,
.planes = {{ { 3, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SBGGR16, {
.name = "SBGGR16",
.format = formats::SBGGR16,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SBGGR16),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 16,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SGBRG16, {
.name = "SGBRG16",
.format = formats::SGBRG16,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SGBRG16),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 16,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SGRBG16, {
.name = "SGRBG16",
.format = formats::SGRBG16,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SGRBG16),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 16,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SRGGB16, {
.name = "SRGGB16",
.format = formats::SRGGB16,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_SRGGB16),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 16,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = false,
.pixelsPerGroup = 2,
.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SBGGR10_IPU3, {
.name = "SBGGR10_IPU3",
.format = formats::SBGGR10_IPU3,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 10,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = true,
/* \todo remember to double this in the ipu3 pipeline handler */
.pixelsPerGroup = 25,
.planes = {{ { 32, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SGBRG10_IPU3, {
.name = "SGBRG10_IPU3",
.format = formats::SGBRG10_IPU3,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGBRG10),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 10,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = true,
.pixelsPerGroup = 25,
.planes = {{ { 32, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SGRBG10_IPU3, {
.name = "SGRBG10_IPU3",
.format = formats::SGRBG10_IPU3,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SGRBG10),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 10,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = true,
.pixelsPerGroup = 25,
.planes = {{ { 32, 1 }, { 0, 0 }, { 0, 0 } }},
} },
{ formats::SRGGB10_IPU3, {
.name = "SRGGB10_IPU3",
.format = formats::SRGGB10_IPU3,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SRGGB10),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 10,
.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
.packed = true,
.pixelsPerGroup = 25,
.planes = {{ { 32, 1 }, { 0, 0 }, { 0, 0 } }},
} },
/* Compressed formats. */
{ formats::MJPEG, {
.name = "MJPEG",
.format = formats::MJPEG,
.v4l2Formats = {
.single = V4L2PixelFormat(V4L2_PIX_FMT_MJPEG),
.multi = V4L2PixelFormat(),
},
.bitsPerPixel = 0,
.colourEncoding = PixelFormatInfo::ColourEncodingYUV,
.packed = false,
.pixelsPerGroup = 1,
.planes = {{ { 1, 1 }, { 0, 0 }, { 0, 0 } }},
} },
};
} /* namespace */
/**
* \fn bool PixelFormatInfo::isValid() const
* \brief Check if the pixel format info is valid
* \return True if the pixel format info is valid, false otherwise
*/
/**
* \brief Retrieve information about a pixel format
* \param[in] format The pixel format
* \return The PixelFormatInfo describing the \a format if known, or an invalid
* PixelFormatInfo otherwise
*/
const PixelFormatInfo &PixelFormatInfo::info(const PixelFormat &format)
{
const auto iter = pixelFormatInfo.find(format);
if (iter == pixelFormatInfo.end()) {
LOG(Formats, Warning)
<< "Unsupported pixel format 0x"
<< utils::hex(format.fourcc());
return pixelFormatInfoInvalid;
}
return iter->second;
}
/**
* \brief Retrieve information about a pixel format
* \param[in] format The V4L2 pixel format
* \return The PixelFormatInfo describing the V4L2 \a format if known, or an
* invalid PixelFormatInfo otherwise
*/
const PixelFormatInfo &PixelFormatInfo::info(const V4L2PixelFormat &format)
{
const auto &info = std::find_if(pixelFormatInfo.begin(), pixelFormatInfo.end(),
[format](auto pair) {
return pair.second.v4l2Formats.single == format ||
pair.second.v4l2Formats.multi == format;
});
if (info == pixelFormatInfo.end())
return pixelFormatInfoInvalid;
return info->second;
}
/**
* \brief Retrieve information about a pixel format
* \param[in] name The name of pixel format
* \return The PixelFormatInfo describing the PixelFormat matching the
* \a name if known, or an invalid PixelFormatInfo otherwise
*/
const PixelFormatInfo &PixelFormatInfo::info(const std::string &name)
{
for (const auto &info : pixelFormatInfo) {
if (info.second.name == name)
return info.second;
}
return pixelFormatInfoInvalid;
}
/**
* \brief Compute the stride
* \param[in] width The width of the line, in pixels
* \param[in] plane The index of the plane whose stride is to be computed
* \param[in] align The stride alignment, in bytes
*
* The stride is the number of bytes necessary to store a full line of a frame,
* including padding at the end of the line. This function takes into account
* the alignment constraints intrinsic to the format (for instance, the
* SGRBG12_CSI2P format stores two 12-bit pixels in 3 bytes, and thus has a
* required stride alignment of 3 bytes). Additional alignment constraints may
* be specified through the \a align parameter, which will cause the stride to
* be rounded up to the next multiple of \a align.
*
* For multi-planar formats, different planes may have different stride values.
* The \a plane parameter selects which plane to compute the stride for.
*
* \return The number of bytes necessary to store a line, or 0 if the
* PixelFormatInfo instance or the \a plane is not valid
*/
unsigned int PixelFormatInfo::stride(unsigned int width, unsigned int plane,
unsigned int align) const
{
if (!isValid()) {
LOG(Formats, Warning) << "Invalid pixel format, stride is zero";
return 0;
}
if (plane > planes.size() || !planes[plane].bytesPerGroup) {
LOG(Formats, Warning) << "Invalid plane index, stride is zero";
return 0;
}
/* ceil(width / pixelsPerGroup) * bytesPerGroup */
unsigned int stride = (width + pixelsPerGroup - 1) / pixelsPerGroup
* planes[plane].bytesPerGroup;
/* ceil(stride / align) * align */
return (stride + align - 1) / align * align;
}
/**
* \brief Compute the number of bytes necessary to store a plane of a frame
* \param[in] size The size of the frame, in pixels
* \param[in] plane The plane index
* \param[in] align The stride alignment, in bytes (1 for default alignment)
*
* The plane size is computed by multiplying the line stride and the frame
* height, taking subsampling and other format characteristics into account.
* Stride alignment constraints may be specified through the \a align parameter.
*
* \sa stride()
*
* \return The number of bytes necessary to store the plane, or 0 if the
* PixelFormatInfo instance is not valid or the plane number isn't valid for the
* format
*/
unsigned int PixelFormatInfo::planeSize(const Size &size, unsigned int plane,
unsigned int align) const
{
unsigned int stride = PixelFormatInfo::stride(size.width, plane, align);
if (!stride)
return 0;
return planeSize(size.height, plane, stride);
}
/**
* \brief Compute the number of bytes necessary to store a plane of a frame
* \param[in] height The height of the frame, in pixels
* \param[in] plane The plane index
* \param[in] stride The plane stride, in bytes
*
* The plane size is computed by multiplying the line stride and the frame
* height, taking subsampling and other format characteristics into account.
* Stride alignment constraints may be specified through the \a align parameter.
*
* \return The number of bytes necessary to store the plane, or 0 if the
* PixelFormatInfo instance is not valid or the plane number isn't valid for the
* format
*/
unsigned int PixelFormatInfo::planeSize(unsigned int height, unsigned int plane,
unsigned int stride) const
{
unsigned int vertSubSample = planes[plane].verticalSubSampling;
if (!vertSubSample)
return 0;
/* stride * ceil(height / verticalSubSampling) */
return stride * ((height + vertSubSample - 1) / vertSubSample);
}
/**
* \brief Compute the number of bytes necessary to store a frame
* \param[in] size The size of the frame, in pixels
* \param[in] align The stride alignment, in bytes (1 for default alignment)
*
* The frame size is computed by adding the size of all planes, as computed by
* planeSize(), using the specified alignment constraints for all planes. For
* more complex stride constraints, use the frameSize() overloaded version that
* takes an array of stride values.
*
* \sa planeSize()
*
* \return The number of bytes necessary to store the frame, or 0 if the
* PixelFormatInfo instance is not valid
*/
unsigned int PixelFormatInfo::frameSize(const Size &size, unsigned int align) const
{
unsigned int sum = 0;
for (const auto &[i, plane] : utils::enumerate(planes)) {
if (plane.bytesPerGroup == 0)
break;
sum += planeSize(size, i, align);
}
return sum;
}
/**
* \brief Compute the number of bytes necessary to store a frame
* \param[in] size The size of the frame, in pixels
* \param[in] strides The strides to use for each plane
*
* This function is an overloaded version that takes custom strides for each
* plane, to be used when the device has custom alignment constraints that
* can't be described by just an alignment value.
*
* \return The number of bytes necessary to store the frame, or 0 if the
* PixelFormatInfo instance is not valid
*/
unsigned int
PixelFormatInfo::frameSize(const Size &size,
const std::array<unsigned int, 3> &strides) const
{
/* stride * ceil(height / verticalSubSampling) */
unsigned int sum = 0;
for (unsigned int i = 0; i < 3; i++) {
unsigned int vertSubSample = planes[i].verticalSubSampling;
if (!vertSubSample)
continue;
sum += strides[i]
* ((size.height + vertSubSample - 1) / vertSubSample);
}
return sum;
}
/**
* \brief Retrieve the number of planes represented by the format
* \return The number of planes used by the format
*/
unsigned int PixelFormatInfo::numPlanes() const
{
unsigned int count = 0;
for (const Plane &p : planes) {
if (p.bytesPerGroup == 0)
break;
count++;
}
return count;
}
} /* namespace libcamera */