summaryrefslogtreecommitdiff
path: root/kms++
diff options
context:
space:
mode:
authorTomi Valkeinen <tomi.valkeinen@ideasonboard.com>2025-02-05 11:28:23 +0200
committerTomi Valkeinen <tomi.valkeinen@ideasonboard.com>2025-03-26 15:44:00 +0200
commita9e7ecab862183399c5947931f2bad69ecec393d (patch)
tree6c02b4e7a4cb19f9af1421afccd87eef1b084d3a /kms++
parent8b1c053359ed7593e43222daccb8c0db8fcc441f (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.h57
-rw-r--r--kms++/src/dumbframebuffer.cpp17
-rw-r--r--kms++/src/omap/omapframebuffer.cpp8
-rw-r--r--kms++/src/pixelformats.cpp540
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