diff options
| author | Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> | 2025-02-05 11:28:23 +0200 |
|---|---|---|
| committer | Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> | 2025-03-26 15:44:00 +0200 |
| commit | a9e7ecab862183399c5947931f2bad69ecec393d (patch) | |
| tree | 6c02b4e7a4cb19f9af1421afccd87eef1b084d3a /kms++ | |
| parent | 8b1c053359ed7593e43222daccb8c0db8fcc441f (diff) | |
PixelFormats: New PixelFormatInfo code
New pixel format code, from Python pixutils library.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Diffstat (limited to 'kms++')
| -rw-r--r-- | kms++/inc/kms++/pixelformats.h | 57 | ||||
| -rw-r--r-- | kms++/src/dumbframebuffer.cpp | 17 | ||||
| -rw-r--r-- | kms++/src/omap/omapframebuffer.cpp | 8 | ||||
| -rw-r--r-- | kms++/src/pixelformats.cpp | 540 |
4 files changed, 377 insertions, 245 deletions
diff --git a/kms++/inc/kms++/pixelformats.h b/kms++/inc/kms++/pixelformats.h index 1b56495..74f69b2 100644 --- a/kms++/inc/kms++/pixelformats.h +++ b/kms++/inc/kms++/pixelformats.h @@ -2,12 +2,16 @@ #include <cstdint> #include <string> -#include <stdexcept> +#include <tuple> +#include <vector> namespace kms { -constexpr uint32_t MakeFourCC(const char* fourcc) +constexpr uint32_t MakeFourCC(const std::string& fourcc) { + if (fourcc.empty()) + return 0; + return fourcc[0] | (fourcc[1] << 8) | (fourcc[2] << 16) | (fourcc[3] << 24); } @@ -88,20 +92,59 @@ inline std::string PixelFormatToFourCC(PixelFormat f) } enum class PixelColorType { + Undefined, RGB, YUV, + RAW, }; struct PixelFormatPlaneInfo { - uint8_t bitspp; - uint8_t xsub; - uint8_t ysub; + constexpr PixelFormatPlaneInfo() = default; + + constexpr PixelFormatPlaneInfo(uint8_t bytes_per_block) + : bytes_per_block(bytes_per_block), pixels_per_block(1), hsub(1), vsub(1) + { + } + + constexpr PixelFormatPlaneInfo(uint8_t bytes_per_block, uint8_t pixels_per_block, + uint8_t hsub, uint8_t vsub) + : bytes_per_block(bytes_per_block), pixels_per_block(pixels_per_block), + hsub(hsub), vsub(vsub) + { + } + + uint8_t bytes_per_block; + uint8_t pixels_per_block; + uint8_t hsub; + uint8_t vsub; }; struct PixelFormatInfo { + constexpr PixelFormatInfo(const std::string& name, const std::string& drm_fourcc, + const std::string& v4l2_4cc, PixelColorType color, + std::tuple<uint8_t, uint8_t> pixel_align, + std::vector<PixelFormatPlaneInfo> planes) + : name(name), drm_fourcc(kms::MakeFourCC(drm_fourcc)), + v4l2_4cc(kms::MakeFourCC(v4l2_4cc)), type(color), + pixel_align(pixel_align), num_planes(planes.size()), planes(planes) + { + } + + std::string name; + uint32_t drm_fourcc; + uint32_t v4l2_4cc; + PixelColorType type; - uint8_t num_planes; - struct PixelFormatPlaneInfo planes[4]; + std::tuple<uint8_t, uint8_t> pixel_align; + + uint8_t num_planes; // this should be dropped, and use 'planes' vector size + std::vector<PixelFormatPlaneInfo> planes; + + std::tuple<uint32_t, uint32_t> align_pixels(uint32_t width, uint32_t height) const; + uint32_t stride(uint32_t width, uint32_t plane = 0, uint32_t align = 1) const; + uint32_t planesize(uint32_t stride, uint32_t height, uint32_t plane = 0) const; + uint32_t framesize(uint32_t width, uint32_t height, uint32_t align = 1) const; + std::tuple<uint32_t, uint32_t, uint32_t> dumb_size(uint32_t width, uint32_t height, uint32_t plane = 0, uint32_t align = 1) const; }; const struct PixelFormatInfo& get_pixel_format_info(PixelFormat format); diff --git a/kms++/src/dumbframebuffer.cpp b/kms++/src/dumbframebuffer.cpp index fc50586..28039b6 100644 --- a/kms++/src/dumbframebuffer.cpp +++ b/kms++/src/dumbframebuffer.cpp @@ -34,22 +34,15 @@ DumbFramebuffer::DumbFramebuffer(Card& card, uint32_t width, uint32_t height, Pi m_num_planes = format_info.num_planes; for (int i = 0; i < format_info.num_planes; ++i) { - const PixelFormatPlaneInfo& pi = format_info.planes[i]; FramebufferPlane& plane = m_planes.at(i); + auto [w, h, bpp] = format_info.dumb_size(width, height); + /* create dumb buffer */ struct drm_mode_create_dumb creq = drm_mode_create_dumb(); - creq.width = width; - creq.height = height / pi.ysub; - /* - * For fully planar YUV buffers, the chroma planes don't combine - * U and V components, their width must thus be divided by the - * horizontal subsampling factor. - */ - if (format_info.type == PixelColorType::YUV && - format_info.num_planes == 3) - creq.width /= pi.xsub; - creq.bpp = pi.bitspp; + creq.width = w; + creq.height = h; + creq.bpp = bpp; r = drmIoctl(card.fd(), DRM_IOCTL_MODE_CREATE_DUMB, &creq); if (r) throw invalid_argument(string("DRM_IOCTL_MODE_CREATE_DUMB failed: ") + strerror(errno)); diff --git a/kms++/src/omap/omapframebuffer.cpp b/kms++/src/omap/omapframebuffer.cpp index 3bea13e..2efe1ec 100644 --- a/kms++/src/omap/omapframebuffer.cpp +++ b/kms++/src/omap/omapframebuffer.cpp @@ -67,9 +67,9 @@ void OmapFramebuffer::Create(uint32_t width, uint32_t height, PixelFormat format uint32_t stride; if (!(buffer_flags & Flags::Tiled)) { - stride = width * pi.bitspp / 8; + stride = format_info.stride(width, i); - uint32_t size = stride * height / pi.ysub; + uint32_t size = format_info.planesize(stride, height, i); bo = omap_bo_new(m_omap_card.dev(), size, flags); if (!bo) @@ -110,13 +110,13 @@ void OmapFramebuffer::Create(uint32_t width, uint32_t height, PixelFormat format throw invalid_argument("bad bitspertiler"); } - uint32_t width_tiler = width * pi.bitspp / bitspertiler; + uint32_t width_tiler = format_info.stride(width, i) / pi.bytes_per_block; bo = omap_bo_new_tiled(m_omap_card.dev(), width_tiler, height, flags); if (!bo) throw invalid_argument(string("omap_bo_new_tiled failed: ") + strerror(errno)); - stride = round_up(width * pi.bitspp / 8, PAGE_SIZE); + stride = format_info.stride(width, i, PAGE_SIZE); } plane.omap_bo = bo; diff --git a/kms++/src/pixelformats.cpp b/kms++/src/pixelformats.cpp index 5f13ef4..8e3a120 100644 --- a/kms++/src/pixelformats.cpp +++ b/kms++/src/pixelformats.cpp @@ -1,4 +1,6 @@ #include <map> +#include <stdexcept> +#include <cassert> #include <kms++/pixelformats.h> @@ -6,228 +8,223 @@ using namespace std; namespace kms { -static const map<PixelFormat, PixelFormatInfo> format_info_array = { - /* YUV packed */ - { PixelFormat::UYVY, { - PixelColorType::YUV, - 1, - { { 16, 2, 1 } }, - } }, - { PixelFormat::YUYV, { - PixelColorType::YUV, - 1, - { { 16, 2, 1 } }, - } }, - { PixelFormat::YVYU, { - PixelColorType::YUV, - 1, - { { 16, 2, 1 } }, - } }, - { PixelFormat::VYUY, { - PixelColorType::YUV, - 1, - { { 16, 2, 1 } }, - } }, - { PixelFormat::Y210, { - PixelColorType::YUV, - 1, - { { 32, 2, 1 } }, - } }, - { PixelFormat::Y212, { - PixelColorType::YUV, - 1, - { { 32, 2, 1 } }, - } }, - { PixelFormat::Y216, { - PixelColorType::YUV, - 1, - { { 32, 2, 1 } }, - } }, - - /* YUV semi-planar */ - { PixelFormat::NV12, { - PixelColorType::YUV, - 2, - { { 8, 1, 1 }, { 8, 2, 2 } }, - } }, - { PixelFormat::NV21, { - PixelColorType::YUV, - 2, - { { 8, 1, 1 }, { 8, 2, 2 } }, - } }, - { PixelFormat::NV16, { - PixelColorType::YUV, - 2, - { { 8, 1, 1 }, { 8, 2, 1 } }, - } }, - { PixelFormat::NV61, { - PixelColorType::YUV, - 2, - { { 8, 1, 1 }, { 8, 2, 1 } }, - } }, - /* YUV planar */ - { PixelFormat::YUV420, { - PixelColorType::YUV, - 3, - { { 8, 1, 1 }, { 8, 2, 2 }, { 8, 2, 2 } }, - } }, - { PixelFormat::YVU420, { - PixelColorType::YUV, - 3, - { { 8, 1, 1 }, { 8, 2, 2 }, { 8, 2, 2 } }, - } }, - { PixelFormat::YUV422, { - PixelColorType::YUV, - 3, - { { 8, 1, 1 }, { 8, 2, 1 }, { 8, 2, 1 } }, - } }, - { PixelFormat::YVU422, { - PixelColorType::YUV, - 3, - { { 8, 1, 1 }, { 8, 2, 1 }, { 8, 2, 1 } }, - } }, - { PixelFormat::YUV444, { - PixelColorType::YUV, - 3, - { { 8, 1, 1 }, { 8, 1, 1 }, { 8, 1, 1 } }, - } }, - { PixelFormat::YVU444, { - PixelColorType::YUV, - 3, - { { 8, 1, 1 }, { 8, 1, 1 }, { 8, 1, 1 } }, - } }, - /* RGB8 */ - { PixelFormat::RGB332, { - PixelColorType::RGB, - 1, - { { 8, 1, 1 } }, - } }, - /* RGB16 */ - { PixelFormat::RGB565, { - PixelColorType::RGB, - 1, - { { 16, 1, 1 } }, - } }, - { PixelFormat::BGR565, { - PixelColorType::RGB, - 1, - { { 16, 1, 1 } }, - } }, - { PixelFormat::XRGB4444, { - PixelColorType::RGB, - 1, - { { 16, 1, 1 } }, - } }, - { PixelFormat::XRGB1555, { - PixelColorType::RGB, - 1, - { { 16, 1, 1 } }, - } }, - { PixelFormat::ARGB4444, { - PixelColorType::RGB, - 1, - { { 16, 1, 1 } }, - } }, - { PixelFormat::ARGB1555, { - PixelColorType::RGB, - 1, - { { 16, 1, 1 } }, - } }, - /* RGB24 */ - { PixelFormat::RGB888, { - PixelColorType::RGB, - 1, - { { 24, 1, 1 } }, - } }, - { PixelFormat::BGR888, { - PixelColorType::RGB, - 1, - { { 24, 1, 1 } }, - } }, - /* RGB32 */ - { PixelFormat::XRGB8888, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - { PixelFormat::XBGR8888, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - { PixelFormat::RGBX8888, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - { PixelFormat::BGRX8888, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - - { PixelFormat::ARGB8888, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - { PixelFormat::ABGR8888, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - { PixelFormat::RGBA8888, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - { PixelFormat::BGRA8888, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - - { PixelFormat::XRGB2101010, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - { PixelFormat::XBGR2101010, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - { PixelFormat::RGBX1010102, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - { PixelFormat::BGRX1010102, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - - { PixelFormat::ARGB2101010, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - { PixelFormat::ABGR2101010, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - { PixelFormat::RGBA1010102, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, - { PixelFormat::BGRA1010102, { - PixelColorType::RGB, - 1, - { { 32, 1, 1 } }, - } }, +static map<PixelFormat, PixelFormatInfo> format_info_array = { + { + PixelFormat::RGB565, { + PixelFormatInfo { + "RGB565", + "RG16", + "RGBP", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::XRGB1555, { + PixelFormatInfo { + "XRGB1555", + "XR15", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::XRGB4444, { + PixelFormatInfo { + "XRGB4444", + "XR12", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::ARGB1555, { + PixelFormatInfo { + "ARGB1555", + "AR15", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::ARGB4444, { + PixelFormatInfo { + "ARGB4444", + "AR12", + "", + PixelColorType::RGB, + { 1, 1 }, + { { 2, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::RGB888, { + PixelFormatInfo { + "RGB888", + "RG24", + "BGR3", + PixelColorType::RGB, + { 1, 1 }, + { { 3, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::BGR888, { + PixelFormatInfo { + "BGR888", + "BG24", + "RGB3", + PixelColorType::RGB, + { 1, 1 }, + { { 3, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::XRGB8888, { + PixelFormatInfo { + "XRGB8888", + "XR24", + "XR24", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::XBGR8888, { + PixelFormatInfo { + "XBGR8888", + "XB24", + "XB24", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::RGBX8888, { + PixelFormatInfo { + "RGBX8888", + "RX24", + "RX24", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::XBGR2101010, { + PixelFormatInfo { + "XBGR2101010", + "XB30", + "RX30", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::ARGB8888, { + PixelFormatInfo { + "ARGB8888", + "AR24", + "AR24", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::ABGR8888, { + PixelFormatInfo { + "ABGR8888", + "AB24", + "AB24", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::RGBA8888, { + PixelFormatInfo { + "RGBA8888", + "RA24", + "RA24", + PixelColorType::RGB, + { 1, 1 }, + { { 4, 1, 1, 1 } }, + } + } + }, + { + PixelFormat::NV12, { + PixelFormatInfo { + "NV12", + "NV12", + "NM12", + PixelColorType::YUV, + { 2, 2 }, + { { 1, 1, 1, 1 }, { 2, 1, 2, 2 } }, + } + } + }, + { + PixelFormat::NV16, { + PixelFormatInfo { + "NV16", + "NV16", + "NM16", + PixelColorType::YUV, + { 2, 1 }, + { { 1, 1, 1, 1 }, { 2, 1, 2, 1 } }, + } + } + }, + { + PixelFormat::YUYV, { + PixelFormatInfo { + "YUYV", + "YUYV", + "YUYV", + PixelColorType::YUV, + { 2, 1 }, + { { 4, 2, 1, 1 } }, + } + } + }, + { + PixelFormat::UYVY, { + PixelFormatInfo { + "UYVY", + "UYVY", + "UYVY", + PixelColorType::YUV, + { 2, 1 }, + { { 4, 2, 1, 1 } }, + } + } + }, }; const struct PixelFormatInfo& get_pixel_format_info(PixelFormat format) @@ -238,4 +235,103 @@ const struct PixelFormatInfo& get_pixel_format_info(PixelFormat format) return format_info_array.at(format); } + +static constexpr uint32_t _div_round_up(uint32_t a, uint32_t b) +{ + return (a + b - 1) / b; +} + +static constexpr uint32_t _align_up(uint32_t a, uint32_t b) +{ + return _div_round_up(a, b) * b; +} + +std::tuple<uint32_t, uint32_t> PixelFormatInfo::align_pixels(uint32_t width, + uint32_t height) const +{ + return { _align_up(width, std::get<0>(pixel_align)), + _align_up(height, std::get<1>(pixel_align)) }; +} + +uint32_t PixelFormatInfo::stride(uint32_t width, uint32_t plane, uint32_t align) const +{ + if (plane >= num_planes) + throw std::runtime_error("Invalid plane number"); + + assert(width % std::get<0>(pixel_align) == 0); + + const auto& pi = planes[plane]; + + assert(width % pi.pixels_per_block == 0); + uint32_t stride = width / pi.pixels_per_block * pi.bytes_per_block; + + assert(stride % pi.hsub == 0); + stride = stride / pi.hsub; + + stride = _align_up(stride, align); + + return stride; +} + +uint32_t PixelFormatInfo::planesize(uint32_t stride, uint32_t height, + uint32_t plane) const +{ + assert(height % std::get<1>(pixel_align) == 0); + + const auto& pi = planes[plane]; + + assert(height % pi.vsub == 0); + + return stride * (height / pi.vsub); +} + +uint32_t PixelFormatInfo::framesize(uint32_t width, uint32_t height, + uint32_t align) const +{ + uint32_t size = 0; + + for (uint32_t i = 0; i < num_planes; ++i) { + uint32_t s = stride(width, i, align); + size += planesize(s, height, i); + } + + return size; +} + +std::tuple<uint32_t, uint32_t, uint32_t> PixelFormatInfo::dumb_size(uint32_t width, + uint32_t height, + uint32_t plane, + uint32_t align) const +{ + /* + Helper function mainly for DRM dumb framebuffer + Returns (width, height, bitspp) tuple which results in a suitable plane + size. + + DRM_IOCTL_MODE_CREATE_DUMB takes a 'bpp' (bits-per-pixel) argument, + which is then used with the width and height to allocate the buffer. + This doesn't work for pixel formats where the average bits-per-pixel + is not an integer (e.g. XV15) + + So, we instead use the bytes_per_block (in bits) as + the 'bpp' argument, and adjust the width accordingly. + */ + + assert(height % std::get<1>(pixel_align) == 0); + + const auto& pi = planes[plane]; + + assert(height % pi.vsub == 0); + + uint32_t stride = PixelFormatInfo::stride(width, plane, align); + + assert(stride % pi.bytes_per_block == 0); + + width = stride / pi.bytes_per_block; + height = height / pi.vsub; + uint32_t bitspp = pi.bytes_per_block * 8; + + return { width, height, bitspp }; +} + } // namespace kms |
