diff options
Diffstat (limited to 'kms++util/src')
| -rw-r--r-- | kms++util/src/color.cpp | 187 | ||||
| -rw-r--r-- | kms++util/src/colorbar.cpp | 139 | ||||
| -rw-r--r-- | kms++util/src/conv-common.h | 177 | ||||
| -rw-r--r-- | kms++util/src/conv-gray.h | 130 | ||||
| -rw-r--r-- | kms++util/src/conv-raw-packed.h | 230 | ||||
| -rw-r--r-- | kms++util/src/conv-raw.h | 202 | ||||
| -rw-r--r-- | kms++util/src/conv-rgb.h | 299 | ||||
| -rw-r--r-- | kms++util/src/conv-yuv-packed.h | 83 | ||||
| -rw-r--r-- | kms++util/src/conv-yuv-planar-packed.h | 125 | ||||
| -rw-r--r-- | kms++util/src/conv-yuv-planar.h | 214 | ||||
| -rw-r--r-- | kms++util/src/conv-yuv-semiplanar.h | 214 | ||||
| -rw-r--r-- | kms++util/src/conv-yuv.h | 147 | ||||
| -rw-r--r-- | kms++util/src/conv.h | 11 | ||||
| -rw-r--r-- | kms++util/src/cpuframebuffer.cpp | 35 | ||||
| -rw-r--r-- | kms++util/src/drawing.cpp | 624 | ||||
| -rw-r--r-- | kms++util/src/extcpuframebuffer.cpp | 48 | ||||
| -rw-r--r-- | kms++util/src/font_8x8.h | 2570 | ||||
| -rw-r--r-- | kms++util/src/opts.cpp | 118 | ||||
| -rw-r--r-- | kms++util/src/resourcemanager.cpp | 221 | ||||
| -rw-r--r-- | kms++util/src/strhelpers.cpp | 13 | ||||
| -rw-r--r-- | kms++util/src/testpat.cpp | 577 |
21 files changed, 6364 insertions, 0 deletions
diff --git a/kms++util/src/color.cpp b/kms++util/src/color.cpp new file mode 100644 index 0000000..c761e40 --- /dev/null +++ b/kms++util/src/color.cpp @@ -0,0 +1,187 @@ +#include <kms++util/color.h> + +namespace kms +{ +RGB::RGB() +{ + r = g = b = 0; + a = 255; +} + +RGB::RGB(uint8_t r, uint8_t g, uint8_t b) + : RGB(255, r, g, b) +{ +} + +RGB::RGB(uint8_t a, uint8_t r, uint8_t g, uint8_t b) +{ + this->r = r; + this->g = g; + this->b = b; + this->a = a; +} + +RGB::RGB(uint32_t argb) +{ + this->b = (argb >> 0) & 0xff; + this->g = (argb >> 8) & 0xff; + this->r = (argb >> 16) & 0xff; + this->a = (argb >> 24) & 0xff; +} + +uint32_t RGB::rgb888() const +{ + return (r << 16) | (g << 8) | (b << 0); +} + +uint32_t RGB::bgr888() const +{ + return (b << 16) | (g << 8) | (r << 0); +} + +uint32_t RGB::argb8888() const +{ + return (a << 24) | (r << 16) | (g << 8) | (b << 0); +} + +uint32_t RGB::abgr8888() const +{ + return (a << 24) | (b << 16) | (g << 8) | (r << 0); +} + +uint32_t RGB::rgba8888() const +{ + return (r << 24) | (g << 16) | (b << 8) | (a << 0); +} + +uint32_t RGB::bgra8888() const +{ + return (b << 24) | (g << 16) | (r << 8) | (a << 0); +} + +uint32_t RGB::argb2101010() const +{ + return ((a >> 6) << 30) | (r << 22) | (g << 12) | (b << 2); +} + +uint32_t RGB::abgr2101010() const +{ + return ((a >> 6) << 30) | (b << 22) | (g << 12) | (r << 2); +} + +uint32_t RGB::rgba1010102() const +{ + return (r << 24) | (g << 14) | (b << 4) | (a >> 6); +} + +uint32_t RGB::bgra1010102() const +{ + return (b << 24) | (g << 14) | (r << 4) | (a >> 6); +} + +uint8_t RGB::rgb332() const +{ + return ((r >> 5) << 5) | ((g >> 5) << 2) | ((b >> 6) << 0); +} + +uint16_t RGB::rgb565() const +{ + return ((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0); +} + +uint16_t RGB::bgr565() const +{ + return ((b >> 3) << 11) | ((g >> 2) << 5) | ((r >> 3) << 0); +} + +uint16_t RGB::argb4444() const +{ + return ((a >> 4) << 12) | ((r >> 4) << 8) | ((g >> 4) << 4) | ((b >> 4) << 0); +} + +uint16_t RGB::argb1555() const +{ + return ((!!a) << 15) | ((r >> 3) << 10) | ((g >> 3) << 5) | ((b >> 3) << 0); +} + +YUV RGB::yuv(YUVType type) const +{ + return YUV(*this, type); +} + +#define CF_ONE (256) +#define CF(a, b, c) \ + { \ + ((int)((a)*CF_ONE)), ((int)((b)*CF_ONE)), ((int)((c)*CF_ONE)) \ + } +#define CLAMP(a) ((a) > (CF_ONE - 1) ? (CF_ONE - 1) : (a) < 0 ? 0 \ + : (a)) + +const int YUVcoef[static_cast<unsigned>(YUVType::MAX)][3][3] = { + [static_cast<unsigned>(YUVType::BT601_Lim)] = { + CF(0.257, 0.504, 0.098), + CF(-0.148, -0.291, 0.439), + CF(0.439, -0.368, -0.071) }, + [static_cast<unsigned>(YUVType::BT601_Full)] = { CF(0.299, 0.587, 0.114), CF(-0.169, -0.331, 0.500), CF(0.500, -0.419, -0.081) }, + [static_cast<unsigned>(YUVType::BT709_Lim)] = { CF(0.1826, 0.6142, 0.0620), CF(-0.1006, -0.3386, 0.4392), CF(0.4392, -0.3989, -0.0403) }, + [static_cast<unsigned>(YUVType::BT709_Full)] = { CF(0.2126, 0.7152, 0.0722), CF(-0.1146, -0.3854, 0.5000), CF(0.5000, -0.4542, -0.0468) }, +}; + +const int YUVoffset[static_cast<unsigned>(YUVType::MAX)][3] = { + [static_cast<unsigned>(YUVType::BT601_Lim)] = CF(0.0625, 0.5, 0.5), + [static_cast<unsigned>(YUVType::BT601_Full)] = CF(0, 0.5, 0.5), + [static_cast<unsigned>(YUVType::BT709_Lim)] = CF(0.0625, 0.5, 0.5), + [static_cast<unsigned>(YUVType::BT709_Full)] = CF(0, 0.5, 0.5), +}; + +YUV::YUV() +{ + y = u = v = a = 0; +} + +YUV::YUV(uint8_t y, uint8_t u, uint8_t v) +{ + this->y = y; + this->u = u; + this->v = v; + this->a = 0; +} + +static inline uint8_t MAKE_YUV_Y(uint8_t r, uint8_t g, uint8_t b, YUVType type) +{ + unsigned tidx = static_cast<unsigned>(type); + + return CLAMP(((YUVcoef[tidx][0][0] * r + YUVcoef[tidx][0][1] * g + + YUVcoef[tidx][0][2] * b + CF_ONE / 2) / + CF_ONE) + + YUVoffset[tidx][0]); +} + +static inline uint8_t MAKE_YUV_U(uint8_t r, uint8_t g, uint8_t b, YUVType type) +{ + unsigned tidx = static_cast<unsigned>(type); + + return CLAMP(((YUVcoef[tidx][1][0] * r + YUVcoef[tidx][1][1] * g + + YUVcoef[tidx][1][2] * b + CF_ONE / 2) / + CF_ONE) + + YUVoffset[tidx][1]); +} + +static inline uint8_t MAKE_YUV_V(uint8_t r, uint8_t g, uint8_t b, YUVType type) +{ + unsigned tidx = static_cast<unsigned>(type); + + return CLAMP(((YUVcoef[tidx][2][0] * r + YUVcoef[tidx][2][1] * g + + YUVcoef[tidx][2][2] * b + CF_ONE / 2) / + CF_ONE) + + YUVoffset[tidx][2]); +} + +YUV::YUV(const RGB& rgb, YUVType type) +{ + this->y = MAKE_YUV_Y(rgb.r, rgb.g, rgb.b, type); + this->u = MAKE_YUV_U(rgb.r, rgb.g, rgb.b, type); + this->v = MAKE_YUV_V(rgb.r, rgb.g, rgb.b, type); + this->a = rgb.a; +} +} // namespace kms diff --git a/kms++util/src/colorbar.cpp b/kms++util/src/colorbar.cpp new file mode 100644 index 0000000..ec59806 --- /dev/null +++ b/kms++util/src/colorbar.cpp @@ -0,0 +1,139 @@ +#include <cstdint> + +#include <kms++/kms++.h> +#include <kms++util/kms++util.h> + +namespace kms +{ +static const RGB colors32[] = { + RGB(255, 255, 255), + RGB(255, 0, 0), + RGB(255, 255, 255), + RGB(0, 255, 0), + RGB(255, 255, 255), + RGB(0, 0, 255), + RGB(255, 255, 255), + RGB(200, 200, 200), + RGB(255, 255, 255), + RGB(100, 100, 100), + RGB(255, 255, 255), + RGB(50, 50, 50), +}; + +static const uint16_t colors16[] = { + colors32[0].rgb565(), + colors32[1].rgb565(), + colors32[2].rgb565(), + colors32[3].rgb565(), + colors32[4].rgb565(), + colors32[5].rgb565(), + colors32[6].rgb565(), + colors32[7].rgb565(), + colors32[8].rgb565(), + colors32[9].rgb565(), + colors32[10].rgb565(), + colors32[11].rgb565(), +}; + +static void drm_draw_color_bar_rgb888(IFramebuffer& buf, int old_xpos, int xpos, int width) +{ + for (unsigned y = 0; y < buf.height(); ++y) { + RGB bcol = colors32[y * ARRAY_SIZE(colors32) / buf.height()]; + uint32_t* line = reinterpret_cast<uint32_t*>(buf.map(0) + buf.stride(0) * y); + + if (old_xpos >= 0) { + for (int x = old_xpos; x < old_xpos + width; ++x) + line[x] = 0; + } + + for (int x = xpos; x < xpos + width; ++x) + line[x] = bcol.argb8888(); + } +} + +static void drm_draw_color_bar_rgb565(IFramebuffer& buf, int old_xpos, int xpos, int width) +{ + static_assert(ARRAY_SIZE(colors32) == ARRAY_SIZE(colors16), "bad colors arrays"); + + for (unsigned y = 0; y < buf.height(); ++y) { + uint16_t bcol = colors16[y * ARRAY_SIZE(colors16) / buf.height()]; + uint16_t* line = reinterpret_cast<uint16_t*>(buf.map(0) + buf.stride(0) * y); + + if (old_xpos >= 0) { + for (int x = old_xpos; x < old_xpos + width; ++x) + line[x] = 0; + } + + for (int x = xpos; x < xpos + width; ++x) + line[x] = bcol; + } +} + +static void drm_draw_color_bar_semiplanar_yuv(IFramebuffer& buf, int old_xpos, int xpos, int width) +{ + const uint8_t colors[] = { + 0xff, + 0x00, + 0xff, + 0x20, + 0xff, + 0x40, + 0xff, + 0x80, + 0xff, + }; + + for (unsigned y = 0; y < buf.height(); ++y) { + unsigned int bcol = colors[y * ARRAY_SIZE(colors) / buf.height()]; + uint8_t* line = reinterpret_cast<uint8_t*>(buf.map(0) + buf.stride(0) * y); + + if (old_xpos >= 0) { + for (int x = old_xpos; x < old_xpos + width; ++x) + line[x] = 0; + } + + for (int x = xpos; x < xpos + width; ++x) + line[x] = bcol; + } +} + +void draw_color_bar(IFramebuffer& buf, int old_xpos, int xpos, int width) +{ + // Skip if the bar is not fully drawable + if (xpos + width > (int)buf.width()) + return; + + if (old_xpos >= 0 && old_xpos + width > (int)buf.width()) + old_xpos = -1; + + switch (buf.format()) { + case PixelFormat::NV12: + case PixelFormat::NV21: + // XXX not right but gets something on the screen + drm_draw_color_bar_semiplanar_yuv(buf, old_xpos, xpos, width); + break; + + case PixelFormat::YUYV: + case PixelFormat::UYVY: + // XXX not right but gets something on the screen + drm_draw_color_bar_rgb565(buf, old_xpos, xpos, width); + break; + + case PixelFormat::RGB565: + drm_draw_color_bar_rgb565(buf, old_xpos, xpos, width); + break; + + case PixelFormat::BGR565: + // XXX not right, red and blue are reversed + drm_draw_color_bar_rgb565(buf, old_xpos, xpos, width); + break; + + case PixelFormat::XRGB8888: + drm_draw_color_bar_rgb888(buf, old_xpos, xpos, width); + break; + + default: + ASSERT(false); + } +} +} // namespace kms diff --git a/kms++util/src/conv-common.h b/kms++util/src/conv-common.h new file mode 100644 index 0000000..837668f --- /dev/null +++ b/kms++util/src/conv-common.h @@ -0,0 +1,177 @@ +#pragma once + +#include <algorithm> +#include <array> + +#define MDSPAN_IMPL_STANDARD_NAMESPACE md +#define MDSPAN_IMPL_PROPOSED_NAMESPACE exp + +#include <mdspan/mdspan.hpp> + +namespace kms +{ + +/* + * Helpers + */ + +template<class T> +constexpr auto make_strided_view(T* data, size_t rows, size_t cols, size_t row_stride_bytes) +{ + assert(row_stride_bytes % sizeof(T) == 0 && "Row stride must be aligned to element size"); + + size_t row_stride = row_stride_bytes / sizeof(T); + std::array<size_t, 2> strides = { row_stride, 1 }; + + auto layout = md::layout_stride::mapping(md::dextents<size_t, 2>(rows, cols), strides); + + return md::mdspan<T, md::dextents<size_t, 2>, md::layout_stride>(data, layout); +} + +template<class T> +constexpr auto make_strided_fb_view(void* data, size_t rows, size_t cols, size_t row_stride_bytes) +{ + return make_strided_view(reinterpret_cast<T*>(data), rows, cols, row_stride_bytes); +} + +// Helper for static loop unrolling +template<size_t Begin, size_t End, typename F> +constexpr void static_for(F&& f) +{ + [&]<size_t... I>(std::index_sequence<I...>) { + (f(std::integral_constant<size_t, Begin + I>{}), ...); + }(std::make_index_sequence<End - Begin>{}); +} + +template<typename T, typename TElement> +concept HasIndexOperatorReturning = requires(T t) { + { t[0] } -> std::convertible_to<const TElement&>; +}; + +template<typename T, typename TElement> +concept Is2Dspan = std::convertible_to<T, md::mdspan<TElement, md::dextents<size_t, 2>>>; + +/* + * Packing and Layouts + */ + +/* This type must be big enough to hold any of the components' value */ +using component_storage_type = uint16_t; + +enum class ComponentType { + X, A, + R, G, B, + Y, Cb, Cr, + Y0, Y1, Y2, + Cb0, Cb1, Cb2, + Cr0, Cr1, Cr2, +}; + +// Describes a single component's bit layout +template<ComponentType Type, size_t BitSize, size_t BitOffset> +struct ComponentLayout { + static constexpr size_t size = BitSize; + static constexpr size_t offset = BitOffset; + static constexpr ComponentType type = Type; + + static_assert(sizeof(component_storage_type) * 8 >= size); + + static constexpr auto get_mask() { return ((1ull << BitSize) - 1) << BitOffset; } + + template<typename TStorage> + static constexpr TStorage pack_value(TStorage value) + { + return (value & ((1ull << BitSize) - 1)) << BitOffset; + } + + template<typename TStorage> + static constexpr component_storage_type unpack_value(TStorage value) + { + return (value >> BitOffset) & ((1ull << BitSize) - 1); + } +}; + +// Describes N components packed into a storage type +template<typename TStorage, typename... Components> +struct PlaneLayout { + using components_tuple = std::tuple<Components...>; + + static constexpr size_t num_components = sizeof...(Components); + + static constexpr size_t total_bits = (Components::size + ...); + static constexpr size_t storage_bits = sizeof(TStorage) * 8; + static_assert(total_bits <= storage_bits, "Components don't fit in storage type"); + + template<size_t N> + static constexpr size_t component_size = std::tuple_element_t<N, components_tuple>::size; + + using storage_type = TStorage; + + static constexpr std::array<ComponentType, sizeof...(Components)> order = { + Components::type... + }; + + template<ComponentType C> + static constexpr size_t component_count() + { + return std::ranges::count(order, C); + } + + template<ComponentType C> + static constexpr size_t find_pos() + { + return std::ranges::find(order, C) - order.begin(); + } + + template<ComponentType C> + static constexpr size_t find_nth_pos(size_t n = 0) + { + auto it = std::ranges::find_if( + order, [&n](ComponentType type) mutable { + return type == C && n-- == 0; + }); + return it - order.begin(); + } + + // Pack values into storage type (separate parameters version) + template<typename... Values> + static constexpr TStorage pack(Values... values) + { + static_assert(sizeof...(values) == num_components, + "Number of values must match number of components"); + return (Components::template pack_value<TStorage>(values) | ...); + } + + // Pack values into storage type (std:array version) + template<typename T, size_t N> + static constexpr TStorage pack(const std::array<T, N>& values) + { + static_assert(N == num_components, + "Number of values must match number of components"); + return [&]<size_t... I>(std::index_sequence<I...>) { + return (Components::template pack_value<TStorage>(values[I]) | ...); + }(std::make_index_sequence<N>{}); + } + + static constexpr std::array<component_storage_type, num_components> + unpack(TStorage value) + { + return [&]<size_t... I>(std::index_sequence<I...>) { + return std::array{ + std::tuple_element_t<I, components_tuple>::template unpack_value< + TStorage>(value)... + }; + }(std::make_index_sequence<num_components>{}); + } +}; + +template<typename... Planes> +class FormatLayout +{ +public: + static constexpr size_t num_planes = sizeof...(Planes); + + template<size_t N> using plane = std::tuple_element_t<N, std::tuple<Planes...>>; +}; + +} // namespace kms diff --git a/kms++util/src/conv-gray.h b/kms++util/src/conv-gray.h new file mode 100644 index 0000000..7fead46 --- /dev/null +++ b/kms++util/src/conv-gray.h @@ -0,0 +1,130 @@ +#pragma once + +#include <vector> + +#include <kms++/framebuffer.h> +#include <kms++util/color16.h> + +#include "conv-common.h" + +namespace kms +{ + +/* + * Grayscale formats + */ + +using Y8_Layout = FormatLayout<PlaneLayout<uint8_t, + ComponentLayout<ComponentType::Y, 8, 0>>>; + +using Y10_Layout = FormatLayout<PlaneLayout<uint16_t, + ComponentLayout<ComponentType::Y, 10, 0>, + ComponentLayout<ComponentType::X, 4, 10>>>; + +using Y12_Layout = FormatLayout<PlaneLayout<uint16_t, + ComponentLayout<ComponentType::Y, 12, 0>, + ComponentLayout<ComponentType::X, 4, 12>>>; + +using Y16_Layout = FormatLayout<PlaneLayout<uint16_t, + ComponentLayout<ComponentType::Y, 16, 0>>>; + +using XYYY2101010_Layout = FormatLayout<PlaneLayout<uint32_t, + ComponentLayout<ComponentType::Y, 10, 0>, // Y0 + ComponentLayout<ComponentType::Y, 10, 10>, // Y1 + ComponentLayout<ComponentType::Y, 10, 20>, // Y2 + ComponentLayout<ComponentType::X, 2, 30> +>>; + +template<typename Layout> class Y_Writer +{ + using Plane = typename Layout::template plane<0>; + using TStorage = typename Plane::storage_type; + + static_assert(Layout::num_planes == 1); + static_assert(Plane::template component_count<ComponentType::Y>() >= 1); + + static constexpr bool has_padding = + Plane::template component_count<ComponentType::X>(); + + static constexpr size_t y_idx = Plane::template find_pos<ComponentType::Y>(); + static constexpr size_t x_idx = + has_padding ? Plane::template find_pos<ComponentType::X>() : 0; + + static constexpr size_t y_shift = 16 - Plane::template component_size<y_idx>; + + static constexpr size_t pixels_in_group = + Plane::template component_count<ComponentType::Y>(); + static constexpr bool is_packed_format = pixels_in_group > 1; + +public: + // Pack and write num_pixels pixels from src_line to dst_line + static void pack_line(HasIndexOperatorReturning<TStorage> auto&& dst_line, + HasIndexOperatorReturning<YUV16> auto&& src_line, + size_t num_pixels) + { + if constexpr (!is_packed_format) { + // Simple case: one Y component per storage unit + for (size_t x = 0; x < num_pixels; x++) { + const YUV16& pix = src_line[x]; + + std::array<component_storage_type, Plane::num_components> + components; + + if constexpr (has_padding) + components[x_idx] = 0; + + components[y_idx] = pix.y >> y_shift; + + dst_line[x] = Plane::pack(components); + } + } else { + write_y_samples(dst_line, src_line, num_pixels); + } + } + + static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, + auto&& generate_line) + { + std::vector<YUV16> linebuf(fb.width()); + + // View to the plane + auto view = make_strided_fb_view<TStorage>(fb.map(0), fb.height(), + fb.width() / pixels_in_group, + fb.stride(0)); + + for (size_t y_src = start_y; y_src <= end_y; y_src++) { + generate_line(y_src, linebuf); + + auto dst = md::submdspan(view, y_src, md::full_extent); + + pack_line(dst, linebuf, fb.width()); + } + } + +private: + template<typename YBuf> + static void write_y_samples(YBuf&& y_view, auto&& linebuf, size_t num_pixels) + { + for (size_t x_src = 0; x_src < num_pixels; x_src += pixels_in_group) { + auto x_dst = x_src / pixels_in_group; + + write_y_group(y_view, linebuf, x_src, x_dst, + std::make_index_sequence<pixels_in_group>{}); + } + } + + template<typename YBuf, size_t... I> + static void write_y_group(YBuf&& y_view, auto&& linebuf, size_t x_src, + size_t x_dst, std::index_sequence<I...>) + { + std::array<component_storage_type, Plane::num_components> y_values{ + static_cast<component_storage_type>( + (linebuf[x_src + I].y >> + (16 - Plane::template component_size<I>)))... + }; + + y_view[x_dst] = Plane::pack(y_values); + } +}; + +} // namespace kms
\ No newline at end of file diff --git a/kms++util/src/conv-raw-packed.h b/kms++util/src/conv-raw-packed.h new file mode 100644 index 0000000..7465c6b --- /dev/null +++ b/kms++util/src/conv-raw-packed.h @@ -0,0 +1,230 @@ +#pragma once + +#include <vector> + +#include <kms++/framebuffer.h> +#include <kms++util/color16.h> + +#include "conv-common.h" +#include "conv-raw.h" + +namespace kms +{ + +/* + * Raw Bayer Packed formats (MIPI CSI-2) + */ + +template<BayerOrder Order, size_t BitDepth> +struct BayerPacked_Layout; + +// 10-bit packed bayer formats - 4 pixels (40 bits) in 5 bytes +template<BayerOrder Order> +struct BayerPacked_Layout<Order, 10> + : public FormatLayout<PlaneLayout<uint8_t, + ComponentLayout<ComponentType::Y, 8, 0>>> +{ + static constexpr BayerOrder bayer_order = Order; + static constexpr size_t bit_depth = 10; + static constexpr size_t pixels_per_group = 4; + static constexpr size_t bytes_per_group = 5; +}; + +// 12-bit packed bayer formats - 2 pixels (24 bits) in 3 bytes +template<BayerOrder Order> +struct BayerPacked_Layout<Order, 12> + : public FormatLayout<PlaneLayout<uint8_t, + ComponentLayout<ComponentType::Y, 8, 0>>> +{ + static constexpr BayerOrder bayer_order = Order; + static constexpr size_t bit_depth = 12; + static constexpr size_t pixels_per_group = 2; + static constexpr size_t bytes_per_group = 3; +}; + +// Convenient aliases for different bit depths +template<BayerOrder Order> using BayerPacked10_Layout = BayerPacked_Layout<Order, 10>; +template<BayerOrder Order> using BayerPacked12_Layout = BayerPacked_Layout<Order, 12>; + +// Format-specific type aliases +using SRGGB10P_Layout = BayerPacked10_Layout<BayerOrder::RGGB>; +using SGBRG10P_Layout = BayerPacked10_Layout<BayerOrder::GBRG>; +using SGRBG10P_Layout = BayerPacked10_Layout<BayerOrder::GRBG>; +using SBGGR10P_Layout = BayerPacked10_Layout<BayerOrder::BGGR>; + +using SRGGB12P_Layout = BayerPacked12_Layout<BayerOrder::RGGB>; +using SGBRG12P_Layout = BayerPacked12_Layout<BayerOrder::GBRG>; +using SGRBG12P_Layout = BayerPacked12_Layout<BayerOrder::GRBG>; +using SBGGR12P_Layout = BayerPacked12_Layout<BayerOrder::BGGR>; + +template<typename Layout> +class BayerPacked_Writer +{ + using Plane = typename Layout::template plane<0>; + using TStorage = typename Plane::storage_type; + + static_assert(Layout::num_planes == 1); + static_assert(std::is_same_v<TStorage, uint8_t>); + + static constexpr BayerOrder bayer_order = Layout::bayer_order; + static constexpr size_t bit_depth = Layout::bit_depth; + static constexpr size_t pixels_per_group = Layout::pixels_per_group; + static constexpr size_t bytes_per_group = Layout::bytes_per_group; + + static constexpr ComponentType get_bayer_component(size_t x, size_t y) + { + const bool x_even = (x % 2) == 0; + const bool y_even = (y % 2) == 0; + + switch (bayer_order) { + case BayerOrder::RGGB: + if (y_even && x_even) return ComponentType::R; + if (y_even && !x_even) return ComponentType::G; + if (!y_even && x_even) return ComponentType::G; + return ComponentType::B; + + case BayerOrder::BGGR: + if (y_even && x_even) return ComponentType::B; + if (y_even && !x_even) return ComponentType::G; + if (!y_even && x_even) return ComponentType::G; + return ComponentType::R; + + case BayerOrder::GRBG: + if (y_even && x_even) return ComponentType::G; + if (y_even && !x_even) return ComponentType::R; + if (!y_even && x_even) return ComponentType::B; + return ComponentType::G; + + case BayerOrder::GBRG: + if (y_even && x_even) return ComponentType::G; + if (y_even && !x_even) return ComponentType::B; + if (!y_even && x_even) return ComponentType::R; + return ComponentType::G; + } + + return ComponentType::Y; // fallback + } + + static uint16_t extract_component(const RGB16& pix, ComponentType component) + { + switch (component) { + case ComponentType::R: + return pix.r; + case ComponentType::G: + return pix.g; + case ComponentType::B: + return pix.b; + default: + return 0; + } + } + + // Pack 10-bit pixels: 4 pixels (40 bits) into 5 bytes + static void pack_10bit_group(uint8_t* dst, const std::array<uint16_t, 4>& values) + { + // Convert from 16-bit to 10-bit values + const uint16_t p0 = values[0] >> 6; + const uint16_t p1 = values[1] >> 6; + const uint16_t p2 = values[2] >> 6; + const uint16_t p3 = values[3] >> 6; + + // Pack MSB 8 bits of each pixel + dst[0] = (p0 >> 2) & 0xFF; + dst[1] = (p1 >> 2) & 0xFF; + dst[2] = (p2 >> 2) & 0xFF; + dst[3] = (p3 >> 2) & 0xFF; + + // Pack LSB 2 bits of each pixel into 5th byte + dst[4] = ((p0 & 0x03) << 6) | + ((p1 & 0x03) << 4) | + ((p2 & 0x03) << 2) | + ((p3 & 0x03) << 0); + } + + // Pack 12-bit pixels: 2 pixels (24 bits) into 3 bytes + static void pack_12bit_group(uint8_t* dst, const std::array<uint16_t, 2>& values) + { + // Convert from 16-bit to 12-bit values + const uint16_t p0 = values[0] >> 4; + const uint16_t p1 = values[1] >> 4; + + // Pack MSB 8 bits of each pixel + dst[0] = (p0 >> 4) & 0xFF; + dst[1] = (p1 >> 4) & 0xFF; + + // Pack LSB 4 bits of each pixel into 3rd byte + dst[2] = ((p0 & 0x0F) << 4) | + ((p1 & 0x0F) << 0); + } + +public: + static void pack_line(HasIndexOperatorReturning<TStorage> auto&& dst_line, + HasIndexOperatorReturning<RGB16> auto&& src_line, + size_t num_pixels, size_t y) + { + if constexpr (bit_depth == 10) { + // Process 4 pixels at a time for 10-bit packed + for (size_t x = 0; x < num_pixels; x += pixels_per_group) { + std::array<uint16_t, 4> pixel_values; + + for (size_t i = 0; i < pixels_per_group && (x + i) < num_pixels; i++) { + const RGB16& pix = src_line[x + i]; + const ComponentType component = get_bayer_component(x + i, y); + pixel_values[i] = extract_component(pix, component); + } + + // Fill remaining pixels with 0 if at end of line + for (size_t i = num_pixels - x; i < pixels_per_group; i++) { + pixel_values[i] = 0; + } + + const size_t group_idx = x / pixels_per_group; + uint8_t* dst_bytes = reinterpret_cast<uint8_t*>(&dst_line[0]); + pack_10bit_group(dst_bytes + group_idx * bytes_per_group, pixel_values); + } + } else if constexpr (bit_depth == 12) { + // Process 2 pixels at a time for 12-bit packed + for (size_t x = 0; x < num_pixels; x += pixels_per_group) { + std::array<uint16_t, 2> pixel_values; + + for (size_t i = 0; i < pixels_per_group && (x + i) < num_pixels; i++) { + const RGB16& pix = src_line[x + i]; + const ComponentType component = get_bayer_component(x + i, y); + pixel_values[i] = extract_component(pix, component); + } + + // Fill remaining pixels with 0 if at end of line + for (size_t i = num_pixels - x; i < pixels_per_group; i++) { + pixel_values[i] = 0; + } + + const size_t group_idx = x / pixels_per_group; + uint8_t* dst_bytes = reinterpret_cast<uint8_t*>(&dst_line[0]); + pack_12bit_group(dst_bytes + group_idx * bytes_per_group, pixel_values); + } + } + } + + static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, + auto&& generate_line) + { + std::vector<RGB16> linebuf(fb.width()); + + // For packed formats, the stride represents bytes per line + // We need to calculate the correct width in bytes for the view + const size_t bytes_per_line = fb.stride(0); + + auto view = make_strided_fb_view<TStorage>(fb.map(0), fb.height(), + bytes_per_line, fb.stride(0)); + + for (size_t y_src = start_y; y_src <= end_y; y_src++) { + generate_line(y_src, linebuf); + + auto dst = md::submdspan(view, y_src, md::full_extent); + + pack_line(dst, linebuf, fb.width(), y_src); + } + } +}; + +} // namespace kms diff --git a/kms++util/src/conv-raw.h b/kms++util/src/conv-raw.h new file mode 100644 index 0000000..a0752d3 --- /dev/null +++ b/kms++util/src/conv-raw.h @@ -0,0 +1,202 @@ +#pragma once + +#include <vector> + +#include <kms++/framebuffer.h> +#include <kms++util/color16.h> + +#include "conv-common.h" + +namespace kms +{ + +/* + * Raw Bayer formats + */ + +enum class BayerOrder { + RGGB, + BGGR, + GRBG, + GBRG +}; + +template<BayerOrder Order, size_t BitDepth> +struct Bayer_Layout; + +// 8-bit bayer formats (uint8_t storage) +template<BayerOrder Order> +struct Bayer_Layout<Order, 8> + : public FormatLayout<PlaneLayout<uint8_t, + ComponentLayout<ComponentType::Y, 8, 0>>> +{ + static constexpr BayerOrder bayer_order = Order; + static constexpr size_t bit_depth = 8; +}; + +// 10-bit bayer formats (uint16_t storage) +template<BayerOrder Order> +struct Bayer_Layout<Order, 10> + : public FormatLayout<PlaneLayout<uint16_t, + ComponentLayout<ComponentType::Y, 10, 0>, + ComponentLayout<ComponentType::X, 6, 10>>> +{ + static constexpr BayerOrder bayer_order = Order; + static constexpr size_t bit_depth = 10; +}; + +// 12-bit bayer formats (uint16_t storage) +template<BayerOrder Order> +struct Bayer_Layout<Order, 12> + : public FormatLayout<PlaneLayout<uint16_t, + ComponentLayout<ComponentType::Y, 12, 0>, + ComponentLayout<ComponentType::X, 4, 12>>> +{ + static constexpr BayerOrder bayer_order = Order; + static constexpr size_t bit_depth = 12; +}; + +// 16-bit bayer formats (uint16_t storage) +template<BayerOrder Order> +struct Bayer_Layout<Order, 16> + : public FormatLayout<PlaneLayout<uint16_t, + ComponentLayout<ComponentType::Y, 16, 0>>> +{ + static constexpr BayerOrder bayer_order = Order; + static constexpr size_t bit_depth = 16; +}; + +// Convenient aliases for different bit depths +template<BayerOrder Order> using Bayer8_Layout = Bayer_Layout<Order, 8>; +template<BayerOrder Order> using Bayer10_Layout = Bayer_Layout<Order, 10>; +template<BayerOrder Order> using Bayer12_Layout = Bayer_Layout<Order, 12>; +template<BayerOrder Order> using Bayer16_Layout = Bayer_Layout<Order, 16>; + +// Format-specific type aliases +using SRGGB8_Layout = Bayer8_Layout<BayerOrder::RGGB>; +using SGBRG8_Layout = Bayer8_Layout<BayerOrder::GBRG>; +using SGRBG8_Layout = Bayer8_Layout<BayerOrder::GRBG>; +using SBGGR8_Layout = Bayer8_Layout<BayerOrder::BGGR>; + +using SRGGB10_Layout = Bayer10_Layout<BayerOrder::RGGB>; +using SGBRG10_Layout = Bayer10_Layout<BayerOrder::GBRG>; +using SGRBG10_Layout = Bayer10_Layout<BayerOrder::GRBG>; +using SBGGR10_Layout = Bayer10_Layout<BayerOrder::BGGR>; + +using SRGGB12_Layout = Bayer12_Layout<BayerOrder::RGGB>; +using SGBRG12_Layout = Bayer12_Layout<BayerOrder::GBRG>; +using SGRBG12_Layout = Bayer12_Layout<BayerOrder::GRBG>; +using SBGGR12_Layout = Bayer12_Layout<BayerOrder::BGGR>; + +using SRGGB16_Layout = Bayer16_Layout<BayerOrder::RGGB>; +using SGBRG16_Layout = Bayer16_Layout<BayerOrder::GBRG>; +using SGRBG16_Layout = Bayer16_Layout<BayerOrder::GRBG>; +using SBGGR16_Layout = Bayer16_Layout<BayerOrder::BGGR>; + +template<typename Layout> +class Bayer_Writer +{ + using Plane = typename Layout::template plane<0>; + using TStorage = typename Plane::storage_type; + + static_assert(Layout::num_planes == 1); + static_assert(Plane::template component_count<ComponentType::Y>() == 1); + + static constexpr BayerOrder bayer_order = Layout::bayer_order; + static constexpr size_t bit_depth = Layout::bit_depth; + static constexpr bool has_padding = Plane::template component_count<ComponentType::X>(); + + static constexpr size_t y_idx = Plane::template find_pos<ComponentType::Y>(); + static constexpr size_t x_idx = has_padding ? Plane::template find_pos<ComponentType::X>() : 0; + + static constexpr size_t y_shift = 16 - Plane::template component_size<y_idx>; + + static constexpr ComponentType get_bayer_component(size_t x, size_t y) + { + const bool x_even = (x % 2) == 0; + const bool y_even = (y % 2) == 0; + + switch (bayer_order) { + case BayerOrder::RGGB: + if (y_even && x_even) return ComponentType::R; + if (y_even && !x_even) return ComponentType::G; + if (!y_even && x_even) return ComponentType::G; + return ComponentType::B; + + case BayerOrder::BGGR: + if (y_even && x_even) return ComponentType::B; + if (y_even && !x_even) return ComponentType::G; + if (!y_even && x_even) return ComponentType::G; + return ComponentType::R; + + case BayerOrder::GRBG: + if (y_even && x_even) return ComponentType::G; + if (y_even && !x_even) return ComponentType::R; + if (!y_even && x_even) return ComponentType::B; + return ComponentType::G; + + case BayerOrder::GBRG: + if (y_even && x_even) return ComponentType::G; + if (y_even && !x_even) return ComponentType::B; + if (!y_even && x_even) return ComponentType::R; + return ComponentType::G; + } + + return ComponentType::Y; // fallback + } + + static uint16_t extract_component(const RGB16& pix, ComponentType component) + { + switch (component) { + case ComponentType::R: + return pix.r; + case ComponentType::G: + return pix.g; + case ComponentType::B: + return pix.b; + default: + return 0; + } + } + +public: + static void pack_line(HasIndexOperatorReturning<TStorage> auto&& dst_line, + HasIndexOperatorReturning<RGB16> auto&& src_line, + size_t num_pixels, size_t y) + { + for (size_t x = 0; x < num_pixels; x++) { + const RGB16& pix = src_line[x]; + + const ComponentType component = get_bayer_component(x, y); + const uint16_t value = extract_component(pix, component); + + std::array<component_storage_type, Plane::num_components> components; + + if constexpr (has_padding) + components[x_idx] = 0; + + components[y_idx] = value >> y_shift; + + dst_line[x] = Plane::pack(components); + } + } + + static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, + auto&& generate_line) + { + std::vector<RGB16> linebuf(fb.width()); + + auto view = make_strided_fb_view<TStorage>(fb.map(0), fb.height(), fb.width(), + fb.stride(0)); + + for (size_t y_src = start_y; y_src <= end_y; y_src++) { + generate_line(y_src, linebuf); + + auto dst = md::submdspan(view, y_src, md::full_extent); + + pack_line(dst, linebuf, fb.width(), y_src); + } + } +}; + +} // namespace kms diff --git a/kms++util/src/conv-rgb.h b/kms++util/src/conv-rgb.h new file mode 100644 index 0000000..6993247 --- /dev/null +++ b/kms++util/src/conv-rgb.h @@ -0,0 +1,299 @@ +#pragma once + +#include <vector> + +#include <kms++/framebuffer.h> +#include <kms++util/color16.h> + +#include "conv-common.h" + +namespace kms +{ + +/* + * RGB + */ + +template<ComponentType C3, size_t size3, size_t offset3, + ComponentType C2, size_t size2, size_t offset2, + ComponentType C1, size_t size1, size_t offset1, + ComponentType C0, size_t size0, size_t offset0> +struct RGB_16_Layout + : public FormatLayout<PlaneLayout<uint16_t, + ComponentLayout<C0, size0, offset0>, + ComponentLayout<C1, size1, offset1>, + ComponentLayout<C2, size2, offset2>, + ComponentLayout<C3, size3, offset3>>> +{ +}; + +using XRGB4444_Layout = RGB_16_Layout< + ComponentType::X, 4, 12, + ComponentType::R, 4, 8, + ComponentType::G, 4, 4, + ComponentType::B, 4, 0 +>; + +using ARGB4444_Layout = RGB_16_Layout< + ComponentType::A, 4, 12, + ComponentType::R, 4, 8, + ComponentType::G, 4, 4, + ComponentType::B, 4, 0 +>; + +using XRGB1555_Layout = RGB_16_Layout< + ComponentType::X, 1, 15, + ComponentType::R, 5, 10, + ComponentType::G, 5, 5, + ComponentType::B, 5, 0 +>; + +using ARGB1555_Layout = RGB_16_Layout< + ComponentType::A, 1, 15, + ComponentType::R, 5, 10, + ComponentType::G, 5, 5, + ComponentType::B, 5, 0 +>; + +using RGB565_Layout = FormatLayout<PlaneLayout<uint16_t, + ComponentLayout<ComponentType::R, 5, 11>, + ComponentLayout<ComponentType::G, 6, 5>, + ComponentLayout<ComponentType::B, 5, 0>> +>; + +using BGR565_Layout = FormatLayout<PlaneLayout<uint16_t, + ComponentLayout<ComponentType::B, 5, 11>, + ComponentLayout<ComponentType::G, 6, 5>, + ComponentLayout<ComponentType::R, 5, 0>> +>; + +using RGB888_Layout = FormatLayout<PlaneLayout<uint32_t, + ComponentLayout<ComponentType::R, 8, 16>, + ComponentLayout<ComponentType::G, 8, 8>, + ComponentLayout<ComponentType::B, 8, 0>> +>; + +using BGR888_Layout = FormatLayout<PlaneLayout<uint32_t, + ComponentLayout<ComponentType::B, 8, 16>, + ComponentLayout<ComponentType::G, 8, 8>, + ComponentLayout<ComponentType::R, 8, 0>> +>; + +template<ComponentType C3, size_t size3, size_t offset3, + ComponentType C2, size_t size2, size_t offset2, + ComponentType C1, size_t size1, size_t offset1, + ComponentType C0, size_t size0, size_t offset0> +struct RGB_32_Layout + : public FormatLayout<PlaneLayout<uint32_t, + ComponentLayout<C0, size0, offset0>, + ComponentLayout<C1, size1, offset1>, + ComponentLayout<C2, size2, offset2>, + ComponentLayout<C3, size3, offset3>>> +{ +}; + +template<ComponentType C3, ComponentType C2, ComponentType C1, ComponentType C0> +struct RGB_8_32_Layout + : public RGB_32_Layout< + C0, 8, 0, + C1, 8, 8, + C2, 8, 16, + C3, 8, 24> +{ +}; + +using XRGB8888_Layout = RGB_8_32_Layout<ComponentType::X, ComponentType::R, ComponentType::G, ComponentType::B>; +using ARGB8888_Layout = RGB_8_32_Layout<ComponentType::A, ComponentType::R, ComponentType::G, ComponentType::B>; + +using XBGR8888_Layout = RGB_8_32_Layout<ComponentType::X, ComponentType::B, ComponentType::G, ComponentType::R>; +using ABGR8888_Layout = RGB_8_32_Layout<ComponentType::A, ComponentType::B, ComponentType::G, ComponentType::R>; + +using RGBX8888_Layout = RGB_8_32_Layout<ComponentType::R, ComponentType::G, ComponentType::B, ComponentType::X>; +using RGBA8888_Layout = RGB_8_32_Layout<ComponentType::R, ComponentType::G, ComponentType::B, ComponentType::A>; + +using BGRX8888_Layout = RGB_8_32_Layout<ComponentType::B, ComponentType::G, ComponentType::R, ComponentType::X>; +using BGRA8888_Layout = RGB_8_32_Layout<ComponentType::B, ComponentType::G, ComponentType::R, ComponentType::A>; + +using XRGB2101010_Layout = RGB_32_Layout< + ComponentType::X, 2, 30, + ComponentType::R, 10, 20, + ComponentType::G, 10, 10, + ComponentType::B, 10, 0 +>; + +using ARGB2101010_Layout = RGB_32_Layout< + ComponentType::A, 2, 30, + ComponentType::R, 10, 20, + ComponentType::G, 10, 10, + ComponentType::B, 10, 0 +>; + +using XBGR2101010_Layout = RGB_32_Layout< + ComponentType::X, 2, 30, + ComponentType::B, 10, 20, + ComponentType::G, 10, 10, + ComponentType::R, 10, 0 +>; + +using ABGR2101010_Layout = RGB_32_Layout< + ComponentType::A, 2, 30, + ComponentType::B, 10, 20, + ComponentType::G, 10, 10, + ComponentType::R, 10, 0 +>; + +using RGBX1010102_Layout = RGB_32_Layout< + ComponentType::R, 10, 22, + ComponentType::G, 10, 12, + ComponentType::B, 10, 2, + ComponentType::X, 2, 0 +>; + +using RGBA1010102_Layout = RGB_32_Layout< + ComponentType::R, 10, 22, + ComponentType::G, 10, 12, + ComponentType::B, 10, 2, + ComponentType::A, 2, 0 +>; + +using BGRX1010102_Layout = RGB_32_Layout< + ComponentType::B, 10, 22, + ComponentType::G, 10, 12, + ComponentType::R, 10, 2, + ComponentType::X, 2, 0 +>; + +using BGRA1010102_Layout = RGB_32_Layout< + ComponentType::B, 10, 22, + ComponentType::G, 10, 12, + ComponentType::R, 10, 2, + ComponentType::A, 2, 0 +>; + +template<typename Layout> +class ARGB_Writer +{ + using Plane = typename Layout::template plane<0>; + using TStorage = typename Plane::storage_type; + + static_assert(Layout::num_planes == 1); + static_assert(Plane::num_components == 3 || Plane::num_components == 4); + + static_assert(Plane::template component_count<ComponentType::R>() == 1); + static_assert(Plane::template component_count<ComponentType::G>() == 1); + static_assert(Plane::template component_count<ComponentType::B>() == 1); + + static constexpr bool has_alpha = Plane::template component_count<ComponentType::A>(); + static constexpr bool has_padding = Plane::template component_count<ComponentType::X>(); + + static constexpr bool needs_packed_access = Plane::total_bits != Plane::storage_bits; + + static constexpr size_t a_idx = Plane::template find_pos<ComponentType::A>(); + static constexpr size_t x_idx = Plane::template find_pos<ComponentType::X>(); + static constexpr size_t r_idx = Plane::template find_pos<ComponentType::R>(); + static constexpr size_t g_idx = Plane::template find_pos<ComponentType::G>(); + static constexpr size_t b_idx = Plane::template find_pos<ComponentType::B>(); + + static constexpr size_t a_shift = has_alpha ? 16 - Plane::template component_size<a_idx> : 0; + static constexpr size_t r_shift = 16 - Plane::template component_size<r_idx>; + static constexpr size_t g_shift = 16 - Plane::template component_size<g_idx>; + static constexpr size_t b_shift = 16 - Plane::template component_size<b_idx>; + + static_assert(Plane::total_bits % 8 == 0); + static constexpr size_t bytes_per_pixel = Plane::total_bits / 8; + +public: + // Pack and write num_pixels pixels from src_line to dst_line + static void pack_line(HasIndexOperatorReturning<TStorage> auto&& dst_line, + HasIndexOperatorReturning<RGB16> auto&& src_line, + size_t num_pixels) + { + for (size_t x = 0; x < num_pixels; x++) { + const RGB16& pix = src_line[x]; + + std::array<component_storage_type, Plane::num_components> + components; + + if constexpr (has_alpha) + components[a_idx] = pix.a >> a_shift; + + if constexpr (has_padding) + components[x_idx] = 0; + + components[r_idx] = pix.r >> r_shift; + components[g_idx] = pix.g >> g_shift; + components[b_idx] = pix.b >> b_shift; + + if constexpr (!needs_packed_access) { + dst_line[x] = Plane::pack(components); + } else { + auto dst_bytes = reinterpret_cast<uint8_t*>(&dst_line[0]); + + TStorage packed = Plane::pack(components); + + memcpy(dst_bytes + x * bytes_per_pixel, &packed, + bytes_per_pixel); + } + } + } + + // Read and unpack num_pixels pixels from src_line to dst_line + static void unpack_line(HasIndexOperatorReturning<RGB16> auto&& dst_line, + HasIndexOperatorReturning<TStorage> auto&& src_line, + size_t num_pixels) + { + for (size_t x = 0; x < num_pixels; x++) { + decltype(Plane::unpack(src_line[x])) components; + + if constexpr (!needs_packed_access) { + components = Plane::unpack(src_line[x]); + } else { + auto src_bytes = + reinterpret_cast<const uint8_t*>(&src_line[0]); + TStorage packed; + + memcpy(&packed, src_bytes + x * bytes_per_pixel, + bytes_per_pixel); + + components = Plane::unpack(packed); + } + + dst_line[x] = RGB16 { + static_cast<uint16_t>(components[r_idx] << r_shift), + static_cast<uint16_t>(components[g_idx] << g_shift), + static_cast<uint16_t>(components[b_idx] << b_shift), + static_cast<uint16_t>(has_alpha ? components[a_idx] << a_shift : 0), + }; + } + } + + static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, + auto&& generate_line) + { + std::vector<RGB16> linebuf(fb.width()); + + // View to the plane + auto view = make_strided_fb_view<TStorage>(fb.map(0), fb.height(), fb.width(), + fb.stride(0)); + + for (size_t y_src = start_y; y_src <= end_y; y_src++) { + generate_line(y_src, linebuf); + + auto dst = md::submdspan(view, y_src, md::full_extent); + + pack_line(dst, linebuf, fb.width()); + } + } + + static void get_line(IFramebuffer& fb, size_t w, size_t h, size_t row, std::span<RGB16> linebuf) + { + auto view = make_strided_fb_view<const TStorage>(fb.map(0), fb.height(), fb.width(), + fb.stride(0)); + + auto src = md::submdspan(view, row, md::full_extent); + + unpack_line(linebuf, src); + } +}; + +} // namespace kms diff --git a/kms++util/src/conv-yuv-packed.h b/kms++util/src/conv-yuv-packed.h new file mode 100644 index 0000000..01278ef --- /dev/null +++ b/kms++util/src/conv-yuv-packed.h @@ -0,0 +1,83 @@ +#pragma once + +#include <vector> + +#include <kms++/framebuffer.h> +#include <kms++util/color16.h> + +#include "conv-common.h" + +namespace kms +{ + +/* YUV Packed */ + +template<ComponentType C0, ComponentType C1, ComponentType C2, ComponentType C3> +struct YUV_Packed_Format + : public FormatLayout< + PlaneLayout<uint32_t, + ComponentLayout<C0, 8, 0>, + ComponentLayout<C1, 8, 8>, + ComponentLayout<C2, 8, 16>, + ComponentLayout<C3, 8, 24> + > + > +{ +}; + +// Define common packed YUV formats +using YUYV_Layout = YUV_Packed_Format<ComponentType::Y0, ComponentType::Cb, + ComponentType::Y1, ComponentType::Cr>; + +using YVYU_Layout = YUV_Packed_Format<ComponentType::Y0, ComponentType::Cr, + ComponentType::Y1, ComponentType::Cb>; + +using UYVY_Layout = YUV_Packed_Format<ComponentType::Cb, ComponentType::Y0, + ComponentType::Cr, ComponentType::Y1>; + +using VYUY_Layout = YUV_Packed_Format<ComponentType::Cr, ComponentType::Y0, + ComponentType::Cb, ComponentType::Y1>; + +template<typename Layout> +class YUVPackedWriter +{ + using Plane = typename Layout::template plane<0>; + using TStorage = typename Plane::storage_type; + + static constexpr size_t y0_pos = Plane::template find_pos<ComponentType::Y0>(); + static constexpr size_t y1_pos = Plane::template find_pos<ComponentType::Y1>(); + static constexpr size_t cb_pos = Plane::template find_pos<ComponentType::Cb>(); + static constexpr size_t cr_pos = Plane::template find_pos<ComponentType::Cr>(); + +public: + static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, + auto&& generate_line) + { + std::vector<YUV16> linebuf(fb.width()); + + auto view = make_strided_fb_view<TStorage>(fb.map(0), fb.height(), + fb.width() / 2, // Two pixels per storage unit + fb.stride(0)); + + for (size_t y = start_y; y <= end_y; y++) { + generate_line(y, linebuf); + + for (size_t x = 0; x < fb.width(); x += 2) { + // Get two pixels + const YUV16& pix0 = linebuf[x]; + const YUV16& pix1 = linebuf[x + 1]; + + std::array<uint8_t, 4> components; + + components[y0_pos] = pix0.y >> 8; + components[y1_pos] = pix1.y >> 8; + components[cb_pos] = ((pix0.u + pix1.u) / 2) >> 8; + components[cr_pos] = ((pix0.v + pix1.v) / 2) >> 8; + + view(y, x / 2) = Plane::pack(components); + } + } + } +}; + +} // namespace kms diff --git a/kms++util/src/conv-yuv-planar-packed.h b/kms++util/src/conv-yuv-planar-packed.h new file mode 100644 index 0000000..481b594 --- /dev/null +++ b/kms++util/src/conv-yuv-planar-packed.h @@ -0,0 +1,125 @@ +#pragma once + +#include <vector> + +#include <kms++/framebuffer.h> +#include <kms++util/color16.h> + +#include "conv-common.h" + +namespace kms +{ +/* YUV Planar Packed (only T430 for now) */ + +struct T430_Layout : FormatLayout < + PlaneLayout<uint32_t, + ComponentLayout<ComponentType::Y, 10, 0>, + ComponentLayout<ComponentType::Y, 10, 10>, + ComponentLayout<ComponentType::Y, 10, 20>, + ComponentLayout<ComponentType::X, 2, 30>>, + PlaneLayout<uint32_t, + ComponentLayout<ComponentType::Cb, 10, 0>, + ComponentLayout<ComponentType::Cb, 10, 10>, + ComponentLayout<ComponentType::Cb, 10, 20>, + ComponentLayout<ComponentType::X, 2, 30>>, + PlaneLayout<uint32_t, + ComponentLayout<ComponentType::Cr, 10, 0>, + ComponentLayout<ComponentType::Cr, 10, 10>, + ComponentLayout<ComponentType::Cr, 10, 20>, + ComponentLayout<ComponentType::X, 2, 30>> + > +{ + static constexpr size_t y_plane = 0; + static constexpr size_t cb_plane = 1; + static constexpr size_t cr_plane = 2; + +}; + +template<typename Format> +class YUVPlanarPackedWriter +{ + using YLayout = typename Format::template plane<Format::y_plane>; + using CbLayout = typename Format::template plane<Format::cb_plane>; + using CrLayout = typename Format::template plane<Format::cr_plane>; + + using TY = typename YLayout::storage_type; + using TCb = typename CbLayout::storage_type; + using TCr = typename CrLayout::storage_type; + + static constexpr size_t pixels_in_group = 3; + + // Helper to extract the correct component based on component type + template<ComponentType CType> + static uint16_t extract_component(const YUV16& pixel) + { + if constexpr (CType == ComponentType::Y) + return pixel.y; + else if constexpr (CType == ComponentType::Cb) + return pixel.u; + else if constexpr (CType == ComponentType::Cr) + return pixel.v; + else + return 0; // For padding or other component types + } + +public: + static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, + auto&& generate_line) + { + // Line buffers + std::vector<YUV16> linebuf(fb.width()); + + // Views to all planes + auto y_buf = make_strided_fb_view<TY>(fb.map(Format::y_plane), + fb.height(), fb.width(), + fb.stride(Format::y_plane)); + + auto cb_buf = make_strided_fb_view<TCb>(fb.map(Format::cb_plane), + fb.height(), fb.width(), + fb.stride(Format::cb_plane)); + + auto cr_buf = make_strided_fb_view<TCr>(fb.map(Format::cr_plane), + fb.height(), fb.width(), + fb.stride(Format::cr_plane)); + + for (size_t y_src = start_y; y_src <= end_y; y_src++) { + generate_line(y_src, linebuf); + + write_samples<YLayout, ComponentType::Y>(md::submdspan(y_buf, y_src, md::full_extent), linebuf, fb.width()); + write_samples<CbLayout, ComponentType::Cb>(md::submdspan(cb_buf, y_src, md::full_extent), linebuf, fb.width()); + write_samples<CrLayout, ComponentType::Cr>(md::submdspan(cr_buf, y_src, md::full_extent), linebuf, fb.width()); + } + } + + +private: + template<typename Plane, ComponentType CType, typename Buf> + static void write_samples(Buf&& view, auto&& linebuf, size_t num_pixels) + { + for (size_t x_src = 0; x_src < num_pixels; x_src += pixels_in_group) { + auto x_dst = x_src / pixels_in_group; + + write_group<Plane, CType>(view, linebuf, x_src, x_dst, + std::make_index_sequence<pixels_in_group>{}); + } + } + + template<typename Plane, ComponentType CType, typename YBuf, size_t... I> + static void write_group(YBuf&& view, auto&& linebuf, size_t x_src, + size_t x_dst, std::index_sequence<I...>) + { + std::array<component_storage_type, Plane::num_components> values{ + static_cast<component_storage_type>( + (extract_component<CType>(linebuf[x_src + I]) >> + (16 - Plane::template component_size<I>)))... + }; + + // Set padding bits to 0 + if constexpr (Plane::template component_count<ComponentType::X>()) + values[Plane::template find_pos<ComponentType::X>()] = 0; + + view[x_dst] = Plane::pack(values); + } +}; + +} // namespace kms diff --git a/kms++util/src/conv-yuv-planar.h b/kms++util/src/conv-yuv-planar.h new file mode 100644 index 0000000..b8e9247 --- /dev/null +++ b/kms++util/src/conv-yuv-planar.h @@ -0,0 +1,214 @@ +#pragma once + +#include <vector> + +#include <kms++/framebuffer.h> +#include <kms++util/color16.h> + +#include "conv-common.h" + +namespace kms +{ +/* YUV Planar */ + +template<ComponentType P1, ComponentType P2, size_t HSubUV = 1, size_t VSubUV = 1> +class YUV_Planar_Layout + : public FormatLayout< + PlaneLayout<uint8_t, ComponentLayout<ComponentType::Y, 8, 0>>, + PlaneLayout<uint8_t, ComponentLayout<P1, 8, 0>>, + PlaneLayout<uint8_t, ComponentLayout<P2, 8, 0>> + > +{ +public: + static constexpr std::array<ComponentType, 2> uv_order = { P1, P2 }; + static constexpr size_t h_sub = HSubUV; + static constexpr size_t v_sub = VSubUV; + + template<ComponentType P> + static constexpr size_t find_plane() + { + return 1 + (std::ranges::find(uv_order, P) - uv_order.begin()); + } + + static constexpr size_t y_plane = 0; // Y is always plane 0 + static constexpr size_t cb_plane = find_plane<ComponentType::Cb>(); + static constexpr size_t cr_plane = find_plane<ComponentType::Cr>(); +}; + +using YUV444_Layout = YUV_Planar_Layout<ComponentType::Cb, ComponentType::Cr>; +using YVU444_Layout = YUV_Planar_Layout<ComponentType::Cr, ComponentType::Cb>; +using YUV422_Layout = YUV_Planar_Layout<ComponentType::Cb, ComponentType::Cr, 2, 1>; +using YVU422_Layout = YUV_Planar_Layout<ComponentType::Cr, ComponentType::Cb, 2, 1>; +using YUV420_Layout = YUV_Planar_Layout<ComponentType::Cb, ComponentType::Cr, 2, 2>; +using YVU420_Layout = YUV_Planar_Layout<ComponentType::Cr, ComponentType::Cb, 2, 2>; + +template<typename Format> +class YUVPlanarWriter +{ + using YLayout = typename Format::template plane<Format::y_plane>; + using CbLayout = typename Format::template plane<Format::cb_plane>; + using CrLayout = typename Format::template plane<Format::cr_plane>; + + using TY = typename YLayout::storage_type; + using TCb = typename CbLayout::storage_type; + using TCr = typename CrLayout::storage_type; +public: + static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, + auto&& generate_line) + { + assert(start_y % Format::v_sub == 0); + assert((end_y + 1) % Format::v_sub == 0); + + // Line buffers + std::vector<YUV16> linebuf_storage(fb.width() * Format::v_sub); + auto linebuf = md::mdspan(linebuf_storage.data(), Format::v_sub, fb.width()); + + // Views to all planes + auto y_buf = make_strided_fb_view<TY>(fb.map(Format::y_plane), fb.height(), + fb.width(), fb.stride(Format::y_plane)); + + auto cb_buf = make_strided_fb_view<TCb>(fb.map(Format::cb_plane), + fb.height() / Format::v_sub, + fb.width() / Format::h_sub, + fb.stride(Format::cb_plane)); + + auto cr_buf = make_strided_fb_view<TCr>(fb.map(Format::cr_plane), + fb.height() / Format::v_sub, + fb.width() / Format::h_sub, + fb.stride(Format::cr_plane)); + + for (size_t y_src = start_y; y_src <= end_y; y_src++) { + size_t y_offset = y_src % Format::v_sub; + + if (y_offset == 0) { + // Fill line buffers + for (size_t buf_y = 0; buf_y < Format::v_sub; buf_y++) { + auto line = md::submdspan(linebuf, buf_y, md::full_extent); + std::span<YUV16> span(line.data_handle(), line.size()); + generate_line(y_src + buf_y, span); + } + } + + // Write Y plane + write_y_line(y_buf, y_src, linebuf, y_offset); + + // Write Cb/Cr planes if we're at a subsampling boundary + if (y_offset == 0) + write_uv_line(cb_buf, cr_buf, linebuf, y_src); + } + } + + static void write_lines(IFramebuffer& fb, size_t start_y, size_t end_y, + Is2Dspan<YUV16> auto&& lines) + { + const size_t height = end_y - start_y + 1; + + if (lines.extent(0) < height || lines.extent(1) < fb.width()) + throw std::invalid_argument("Source line buffer too small"); + + assert(start_y % Format::v_sub == 0); + assert((end_y + 1) % Format::v_sub == 0); + + // Views to all planes + auto y_buf = make_strided_fb_view<TY>(fb.map(Format::y_plane), fb.height(), + fb.width(), fb.stride(Format::y_plane)); + + auto cb_buf = make_strided_fb_view<TCb>(fb.map(Format::cb_plane), + fb.height() / Format::v_sub, + fb.width() / Format::h_sub, + fb.stride(Format::cb_plane)); + + auto cr_buf = make_strided_fb_view<TCr>(fb.map(Format::cr_plane), + fb.height() / Format::v_sub, + fb.width() / Format::h_sub, + fb.stride(Format::cr_plane)); + + for (size_t y_src = start_y; y_src <= end_y; y_src++) { + size_t y_offset = y_src % Format::v_sub; + + // Write Y plane + write_y_line(y_buf, y_src, lines, y_offset); + + // Write Cb/Cr planes if we're at a subsampling boundary + if (y_offset == 0) + write_uv_line(cb_buf, cr_buf, lines, y_src); + } + } + +private: + template<typename YBuf> + static void write_y_line(YBuf& y_buf, size_t y_src, auto& linebuf, size_t y_offset) + { + for (size_t x = 0; x < linebuf.extent(1); x++) + y_buf(y_src, x) = YLayout::pack(linebuf(y_offset, x).y >> 8); + } + + template<typename UVBuf> + static void write_uv_line(UVBuf& cb_buf, UVBuf& cr_buf, auto& linebuf, size_t y_src) + { + const size_t uv_y = y_src / Format::v_sub; + + for (size_t x = 0; x < linebuf.extent(1); x += Format::h_sub) { + // Average subsampled region + uint32_t u_sum = 0, v_sum = 0; + for (size_t y = 0; y < Format::v_sub; y++) { + for (size_t x_off = 0; x_off < Format::h_sub; x_off++) { + u_sum += linebuf(y, x + x_off).u; + v_sum += linebuf(y, x + x_off).v; + } + } + + const size_t total_samples = Format::h_sub * Format::v_sub; + const size_t uv_x = x / Format::h_sub; + + cb_buf(uv_y, uv_x) = CbLayout::pack(u_sum / total_samples >> 8); + cr_buf(uv_y, uv_x) = CrLayout::pack(v_sum / total_samples >> 8); + } + } + +public: + static void read_lines(IFramebuffer& fb, size_t start_y, size_t end_y, + Is2Dspan<YUV16> auto&& dest) + { + const size_t height = end_y - start_y + 1; + + if (dest.extent(0) < height || dest.extent(1) < fb.width()) + throw std::invalid_argument("Destination line buffer too small"); + + assert(start_y % Format::v_sub == 0); + assert((end_y + 1) % Format::v_sub == 0); + + auto y_buf = make_strided_fb_view<const TY>(fb.map(Format::y_plane), fb.height(), + fb.width(), fb.stride(Format::y_plane)); + + auto cb_buf = make_strided_fb_view<const TCb>(fb.map(Format::cb_plane), + fb.height() / Format::v_sub, + fb.width() / Format::h_sub, + fb.stride(Format::cb_plane)); + + auto cr_buf = make_strided_fb_view<const TCr>(fb.map(Format::cr_plane), + fb.height() / Format::v_sub, + fb.width() / Format::h_sub, + fb.stride(Format::cr_plane)); + + for (size_t y = start_y; y <= end_y; y++) { + for (size_t x = 0; x < fb.width(); x++) { + const auto y_val = YLayout::unpack(y_buf(y, x))[0]; + + const size_t uv_y = y / Format::v_sub; + const size_t uv_x = x / Format::h_sub; + + const auto u_val = CbLayout::unpack(cb_buf(uv_y, uv_x))[0]; + const auto v_val = CrLayout::unpack(cr_buf(uv_y, uv_x))[0]; + + dest(y - start_y, x) = YUV16 { + static_cast<uint16_t>(y_val << 8), + static_cast<uint16_t>(u_val << 8), + static_cast<uint16_t>(v_val << 8) + }; + } + } + } +}; + +} // namespace kms diff --git a/kms++util/src/conv-yuv-semiplanar.h b/kms++util/src/conv-yuv-semiplanar.h new file mode 100644 index 0000000..81117c1 --- /dev/null +++ b/kms++util/src/conv-yuv-semiplanar.h @@ -0,0 +1,214 @@ +#pragma once + +#include <vector> + +#include <kms++/framebuffer.h> +#include <kms++util/color16.h> + +#include "conv-common.h" + +namespace kms +{ + +/* Semiplanar YUV */ + +template<ComponentType C0, ComponentType C1, size_t HSub, size_t VSub> +struct NV12_Family_Layout + : public FormatLayout< + PlaneLayout<uint8_t, + ComponentLayout<ComponentType::Y, 8, 0> + >, + PlaneLayout<uint16_t, + ComponentLayout<C0, 8, 0>, + ComponentLayout<C1, 8, 8> + > + > +{ + static constexpr size_t h_sub = HSub; + static constexpr size_t v_sub = VSub; +}; + +using NV12_Layout = NV12_Family_Layout<ComponentType::Cb, ComponentType::Cr, 2, 2>; +using NV21_Layout = NV12_Family_Layout<ComponentType::Cr, ComponentType::Cb, 2, 2>; + +using NV16_Layout = NV12_Family_Layout<ComponentType::Cb, ComponentType::Cr, 2, 1>; +using NV61_Layout = NV12_Family_Layout<ComponentType::Cr, ComponentType::Cb, 2, 1>; + +template<size_t HSub, size_t VSub> +struct P030_Family_Layout + : public FormatLayout< + PlaneLayout<uint32_t, + ComponentLayout<ComponentType::Y, 10, 0>, + ComponentLayout<ComponentType::Y, 10, 10>, + ComponentLayout<ComponentType::Y, 10, 20> + >, + PlaneLayout<uint64_t, + ComponentLayout<ComponentType::Cb, 10, 0>, + ComponentLayout<ComponentType::Cr, 10, 10>, + ComponentLayout<ComponentType::Cb, 10, 20>, + ComponentLayout<ComponentType::Cr, 10, 32>, + ComponentLayout<ComponentType::Cb, 10, 42>, + ComponentLayout<ComponentType::Cr, 10, 52> + > + > +{ + static constexpr size_t h_sub = HSub; + static constexpr size_t v_sub = VSub; +}; + +using P030_Layout = P030_Family_Layout<2, 2>; +using P230_Layout = P030_Family_Layout<2, 1>; + +template<size_t HSub, size_t VSub> +struct SubsampleHelper { + template<typename View> static constexpr auto subsample(const View& view, size_t group_idx) + { + uint32_t sum = 0; + + static_for<0, HSub>([&](auto x) { sum += view(0, group_idx + x); }); + + if constexpr (VSub > 1) { + static_for<1, VSub>([&](auto y) { + static_for<0, HSub>([&](auto x) { sum += view(y, group_idx + x); }); + }); + } + + return sum / (HSub * VSub); + } +}; + +template<typename Layout> +class YUVSemiPlanarWriter +{ + static_assert(Layout::num_planes == 2); + + static constexpr size_t h_sub = Layout::h_sub; + static constexpr size_t v_sub = Layout::v_sub; + + using YLayout = typename Layout::template plane<0>; + using UVLayout = typename Layout::template plane<1>; + + using TY = typename YLayout::storage_type; + using TCrCb = typename UVLayout::storage_type; + + static constexpr size_t pixels_in_group = YLayout::template component_count<ComponentType::Y>(); + static_assert(pixels_in_group == UVLayout::template component_count<ComponentType::Cb>()); + static_assert(pixels_in_group == UVLayout::template component_count<ComponentType::Cr>()); + +public: + static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, + auto&& generate_line) + { + assert(start_y % v_sub == 0); + assert((end_y + 1) % v_sub == 0); + if (fb.width() % pixels_in_group != 0) + throw std::invalid_argument("FB width doesn't align to pixel format"); + + // Line buffers + std::vector<YUV16> linebuf_storage(fb.width() * v_sub); + auto linebuf = md::mdspan(linebuf_storage.data(), v_sub, fb.width()); + + // Views to the planes + auto y_view = make_strided_fb_view<TY>(fb.map(0), fb.height(), + fb.width() / pixels_in_group, fb.stride(0)); + + auto uv_view = make_strided_fb_view<TCrCb>(fb.map(1), fb.height() / v_sub, + fb.width() / pixels_in_group / h_sub, + fb.stride(1)); + + for (size_t y_src = start_y; y_src <= end_y; y_src++) { + size_t y_offset = y_src % v_sub; + + if (y_offset == 0) { + // Fill line buffers + for (size_t y_off = 0; y_off < v_sub; y_off++) { + auto line = md::submdspan(linebuf, y_off, md::full_extent); + std::span<YUV16> span(line.data_handle(), line.size()); + generate_line(y_src + y_off, span); + } + } + + // Write Y values from the line buffer + write_y_samples(md::submdspan(y_view, y_src, md::full_extent), + md::submdspan(linebuf, y_offset, md::full_extent)); + + if (y_offset == 0) { + // Write UV values from the line buffers + write_uv_samples(uv_view, linebuf, y_src); + } + } + } + +private: + + template<typename YBuf> + static void write_y_samples(YBuf&& y_view, auto&& linebuf) + { + for (size_t x_src = 0; x_src < linebuf.extent(1); x_src += pixels_in_group) { + auto x_dst = x_src / pixels_in_group; + + write_y_group(y_view, linebuf, x_src, x_dst, + std::make_index_sequence<pixels_in_group>{}); + } + } + + template<typename YBuf, size_t... I> + static void write_y_group(YBuf&& y_view, auto&& linebuf, size_t x_src, + size_t x_dst, std::index_sequence<I...>) + { + std::array<component_storage_type, YLayout::num_components> y_values{ + static_cast<component_storage_type>((linebuf(x_src + I).y >> (16 - YLayout::template component_size<I>)))... + }; + + y_view(x_dst) = YLayout::pack(y_values); + } + + template<typename UVBuf> + static void write_uv_samples(UVBuf& uv_view, auto& linebuf, size_t y_src) + { + for (size_t x_src = 0; x_src < linebuf.extent(1); x_src += pixels_in_group * h_sub) { + const size_t y_offset = 0; + auto y_dst = (y_src + y_offset) / v_sub; + auto x_dst = x_src / (pixels_in_group * h_sub); + + auto group_view = md::submdspan(linebuf, std::tuple(y_offset, y_offset + v_sub), + std::tuple(x_src, x_src + h_sub * pixels_in_group)); + + write_uv_group(uv_view, group_view, y_dst, x_dst, + std::make_index_sequence<pixels_in_group>{}); + } + } + + template<typename UVBuf, size_t... I> + static void write_uv_group(UVBuf& uv_view, auto& group_view, size_t y_dst, size_t x_dst, + std::index_sequence<I...>) + { + std::array<component_storage_type, UVLayout::num_components> uv_values; + + ( + [&]<size_t i>() { + constexpr size_t group_idx = i * h_sub; + + constexpr size_t u_idx = UVLayout::template find_nth_pos<ComponentType::Cb>(i); + constexpr size_t v_idx = UVLayout::template find_nth_pos<ComponentType::Cr>(i); + + auto u = SubsampleHelper<h_sub, v_sub>::subsample( + [&group_view](size_t y, size_t x) { return group_view(y, x).u; }, + group_idx); + + auto v = SubsampleHelper<h_sub, v_sub>::subsample( + [&group_view](size_t y, size_t x) { return group_view(y, x).v; }, + group_idx); + + uv_values[u_idx] = + u >> (16 - UVLayout::template component_size<u_idx>); + uv_values[v_idx] = + v >> (16 - UVLayout::template component_size<v_idx>); + }.template operator()<I>(), + ...); + + uv_view(y_dst, x_dst) = UVLayout::pack(uv_values); + } +}; + +} // namespace kms diff --git a/kms++util/src/conv-yuv.h b/kms++util/src/conv-yuv.h new file mode 100644 index 0000000..1695129 --- /dev/null +++ b/kms++util/src/conv-yuv.h @@ -0,0 +1,147 @@ +#pragma once + +#include <vector> + +#include <kms++/framebuffer.h> +#include <kms++util/color16.h> + +#include "conv-common.h" + +namespace kms +{ + +using XVUY2101010_Layout = + FormatLayout<PlaneLayout<uint32_t, + ComponentLayout<ComponentType::Y, 10, 0>, + ComponentLayout<ComponentType::Cb, 10, 10>, + ComponentLayout<ComponentType::Cr, 10, 20>, + ComponentLayout<ComponentType::X, 2, 30>>>; + +template<typename Layout> +class YUV_Writer +{ + using Plane = typename Layout::template plane<0>; + using TStorage = typename Plane::storage_type; + + static_assert(Layout::num_planes == 1); + static_assert(Plane::num_components == 3 || Plane::num_components == 4); + + static_assert(Plane::template component_count<ComponentType::Y>() == 1); + static_assert(Plane::template component_count<ComponentType::Cb>() == 1); + static_assert(Plane::template component_count<ComponentType::Cr>() == 1); + + static constexpr bool has_alpha = Plane::template component_count<ComponentType::A>(); + static constexpr bool has_padding = Plane::template component_count<ComponentType::X>(); + + static constexpr bool needs_packed_access = Plane::total_bits != Plane::storage_bits; + + static constexpr size_t a_idx = Plane::template find_pos<ComponentType::A>(); + static constexpr size_t x_idx = Plane::template find_pos<ComponentType::X>(); + static constexpr size_t y_idx = Plane::template find_pos<ComponentType::Y>(); + static constexpr size_t cb_idx = Plane::template find_pos<ComponentType::Cb>(); + static constexpr size_t cr_idx = Plane::template find_pos<ComponentType::Cr>(); + + static constexpr size_t a_shift = has_alpha ? 16 - Plane::template component_size<a_idx> : 0; + static constexpr size_t x_shift = has_padding ? 16 - Plane::template component_size<x_idx> : 0; + static constexpr size_t y_shift = 16 - Plane::template component_size<y_idx>; + static constexpr size_t cb_shift = 16 - Plane::template component_size<cb_idx>; + static constexpr size_t cr_shift = 16 - Plane::template component_size<cr_idx>; + + static_assert(Plane::total_bits % 8 == 0); + static constexpr size_t bytes_per_pixel = Plane::total_bits / 8; + +public: + // Pack and write num_pixels pixels from src_line to dst_line + static void pack_line(HasIndexOperatorReturning<TStorage> auto&& dst_line, + HasIndexOperatorReturning<YUV16> auto&& src_line, + size_t num_pixels) + { + for (size_t x = 0; x < num_pixels; x++) { + const YUV16& pix = src_line[x]; + + std::array<component_storage_type, Plane::num_components> + components; + + if constexpr (has_alpha) + components[a_idx] = pix.a >> a_shift; + + if constexpr (has_padding) + components[x_idx] = 0; + + components[y_idx] = pix.y >> y_shift; + components[cb_idx] = pix.u >> cb_shift; + components[cr_idx] = pix.v >> cr_shift; + + if constexpr (!needs_packed_access) { + dst_line[x] = Plane::pack(components); + } else { + auto dst_bytes = reinterpret_cast<uint8_t*>(&dst_line[0]); + + TStorage packed = Plane::pack(components); + + memcpy(dst_bytes + x * bytes_per_pixel, &packed, + bytes_per_pixel); + } + } + } + + // Read and unpack num_pixels pixels from src_line to dst_line + static void unpack_line(HasIndexOperatorReturning<YUV16> auto&& dst_line, + HasIndexOperatorReturning<TStorage> auto&& src_line, + size_t num_pixels) + { + for (size_t x = 0; x < num_pixels; x++) { + decltype(Plane::unpack(src_line[x])) components; + + if constexpr (!needs_packed_access) { + components = Plane::unpack(src_line[x]); + } else { + auto src_bytes = + reinterpret_cast<const uint8_t*>(&src_line[0]); + TStorage packed; + + memcpy(&packed, src_bytes + x * bytes_per_pixel, + bytes_per_pixel); + + components = Plane::unpack(packed); + } + + dst_line[x] = YUV16 { + static_cast<uint16_t>(components[y_idx] << y_shift), + static_cast<uint16_t>(components[cb_idx] << cb_shift), + static_cast<uint16_t>(components[cr_idx] << cr_shift), + static_cast<uint16_t>(has_alpha ? components[a_idx] << a_shift : 0), + }; + } + } + + static void write_pattern(IFramebuffer& fb, size_t start_y, size_t end_y, + auto&& generate_line) + { + std::vector<YUV16> linebuf(fb.width()); + + // View to the plane + auto view = make_strided_fb_view<TStorage>(fb.map(0), fb.height(), fb.width(), + fb.stride(0)); + + for (size_t y_src = start_y; y_src <= end_y; y_src++) { + generate_line(y_src, linebuf); + + auto dst = md::submdspan(view, y_src, md::full_extent); + + pack_line(dst, linebuf, fb.width()); + } + } + + static void get_line(IFramebuffer& fb, size_t w, size_t h, size_t row, std::span<YUV16> linebuf) + { + auto view = make_strided_fb_view<const TStorage>(fb.map(0), fb.height(), fb.width(), + fb.stride(0)); + + auto src = md::submdspan(view, row, md::full_extent); + + unpack_line(linebuf, src); + } +}; + +} // namespace kms diff --git a/kms++util/src/conv.h b/kms++util/src/conv.h new file mode 100644 index 0000000..82608dd --- /dev/null +++ b/kms++util/src/conv.h @@ -0,0 +1,11 @@ +#pragma once + +#include "conv-rgb.h" +#include "conv-yuv.h" +#include "conv-yuv-packed.h" +#include "conv-yuv-semiplanar.h" +#include "conv-yuv-planar.h" +#include "conv-yuv-planar-packed.h" +#include "conv-gray.h" +#include "conv-raw.h" +#include "conv-raw-packed.h" diff --git a/kms++util/src/cpuframebuffer.cpp b/kms++util/src/cpuframebuffer.cpp new file mode 100644 index 0000000..64ad531 --- /dev/null +++ b/kms++util/src/cpuframebuffer.cpp @@ -0,0 +1,35 @@ +#include <map> + +#include <kms++util/cpuframebuffer.h> + +using namespace std; + +namespace kms +{ +CPUFramebuffer::CPUFramebuffer(uint32_t width, uint32_t height, PixelFormat format) + : m_width(width), m_height(height), m_format(format) +{ + const PixelFormatInfo& format_info = get_pixel_format_info(m_format); + + m_num_planes = format_info.num_planes; + + for (unsigned i = 0; i < format_info.num_planes; ++i) { + FramebufferPlane& plane = m_planes[i]; + + plane.stride = format_info.stride(width, i); + plane.size = format_info.planesize(plane.stride, height, i); + plane.offset = 0; + plane.map = new uint8_t[plane.size]; + } +} + +CPUFramebuffer::~CPUFramebuffer() +{ + for (unsigned i = 0; i < m_num_planes; ++i) { + const FramebufferPlane& plane = m_planes[i]; + + delete[] plane.map; + } +} + +} // namespace kms diff --git a/kms++util/src/drawing.cpp b/kms++util/src/drawing.cpp new file mode 100644 index 0000000..df02485 --- /dev/null +++ b/kms++util/src/drawing.cpp @@ -0,0 +1,624 @@ + +#include <cmath> + +#include <kms++/kms++.h> +#include <kms++util/kms++util.h> +#include <kms++util/endian.h> + +using namespace std; + +namespace kms +{ +void draw_rgb_pixel(IFramebuffer& buf, unsigned x, unsigned y, RGB color) +{ + if (x >= buf.width() || y >= buf.height()) + throw runtime_error("attempt to draw outside the buffer"); + + switch (buf.format()) { + case PixelFormat::XRGB8888: + case PixelFormat::ARGB8888: { + uint32_t* p = reinterpret_cast<uint32_t*>(buf.map(0) + buf.stride(0) * y + x * 4); + *p = color.argb8888(); + break; + } + case PixelFormat::XBGR8888: + case PixelFormat::ABGR8888: { + uint32_t* p = reinterpret_cast<uint32_t*>(buf.map(0) + buf.stride(0) * y + x * 4); + *p = color.abgr8888(); + break; + } + case PixelFormat::RGBX8888: + case PixelFormat::RGBA8888: { + uint32_t* p = reinterpret_cast<uint32_t*>(buf.map(0) + buf.stride(0) * y + x * 4); + *p = color.rgba8888(); + break; + } + case PixelFormat::BGRX8888: + case PixelFormat::BGRA8888: { + uint32_t* p = reinterpret_cast<uint32_t*>(buf.map(0) + buf.stride(0) * y + x * 4); + *p = color.bgra8888(); + break; + } + case PixelFormat::XRGB2101010: + case PixelFormat::ARGB2101010: { + uint32_t* p = reinterpret_cast<uint32_t*>(buf.map(0) + buf.stride(0) * y + x * 4); + *p = color.argb2101010(); + break; + } + case PixelFormat::XBGR2101010: + case PixelFormat::ABGR2101010: { + uint32_t* p = reinterpret_cast<uint32_t*>(buf.map(0) + buf.stride(0) * y + x * 4); + *p = color.abgr2101010(); + break; + } + case PixelFormat::RGBX1010102: + case PixelFormat::RGBA1010102: { + uint32_t* p = reinterpret_cast<uint32_t*>(buf.map(0) + buf.stride(0) * y + x * 4); + *p = color.rgba1010102(); + break; + } + case PixelFormat::BGRX1010102: + case PixelFormat::BGRA1010102: { + uint32_t* p = reinterpret_cast<uint32_t*>(buf.map(0) + buf.stride(0) * y + x * 4); + *p = color.bgra1010102(); + break; + } + case PixelFormat::RGB888: { + uint8_t* p = buf.map(0) + buf.stride(0) * y + x * 3; + p[0] = color.b; + p[1] = color.g; + p[2] = color.r; + break; + } + case PixelFormat::BGR888: { + uint8_t* p = buf.map(0) + buf.stride(0) * y + x * 3; + p[0] = color.r; + p[1] = color.g; + p[2] = color.b; + break; + } + case PixelFormat::RGB332: { + uint8_t* p = reinterpret_cast<uint8_t*>(buf.map(0) + buf.stride(0) * y + x); + *p = color.rgb332(); + break; + } + case PixelFormat::RGB565: { + uint16_t* p = reinterpret_cast<uint16_t*>(buf.map(0) + buf.stride(0) * y + x * 2); + *p = color.rgb565(); + break; + } + case PixelFormat::BGR565: { + uint16_t* p = reinterpret_cast<uint16_t*>(buf.map(0) + buf.stride(0) * y + x * 2); + *p = color.bgr565(); + break; + } + case PixelFormat::XRGB4444: + case PixelFormat::ARGB4444: { + uint16_t* p = reinterpret_cast<uint16_t*>(buf.map(0) + buf.stride(0) * y + x * 2); + *p = color.argb4444(); + break; + } + case PixelFormat::XRGB1555: + case PixelFormat::ARGB1555: { + uint16_t* p = reinterpret_cast<uint16_t*>(buf.map(0) + buf.stride(0) * y + x * 2); + *p = color.argb1555(); + break; + } + default: + throw std::invalid_argument("invalid pixelformat"); + } +} + +void draw_yuv444_pixel(IFramebuffer& buf, unsigned x, unsigned y, YUV yuv) +{ + if (x >= buf.width() || y >= buf.height()) + throw runtime_error("attempt to draw outside the buffer"); + + uint8_t* py = reinterpret_cast<uint8_t*>(buf.map(0) + buf.stride(0) * y + x); + uint8_t* pu = reinterpret_cast<uint8_t*>(buf.map(1) + buf.stride(1) * y + x); + uint8_t* pv = reinterpret_cast<uint8_t*>(buf.map(2) + buf.stride(2) * y + x); + + switch (buf.format()) { + case PixelFormat::YUV444: + py[0] = yuv.y; + pu[0] = yuv.u; + pv[0] = yuv.v; + break; + + case PixelFormat::YVU444: + py[0] = yuv.y; + pu[0] = yuv.v; + pv[0] = yuv.u; + break; + + default: + throw std::invalid_argument("invalid pixelformat"); + } +} + +static void draw_yuv422_packed_macropixel(IFramebuffer& buf, unsigned x, unsigned y, + YUV yuv1, YUV yuv2) +{ + uint8_t* p = reinterpret_cast<uint8_t*>(buf.map(0) + buf.stride(0) * y + x * 2); + + uint8_t y0 = yuv1.y; + uint8_t y1 = yuv2.y; + uint8_t u = (yuv1.u + yuv2.u) / 2; + uint8_t v = (yuv1.v + yuv2.v) / 2; + + switch (buf.format()) { + case PixelFormat::UYVY: + p[0] = u; + p[1] = y0; + p[2] = v; + p[3] = y1; + break; + + case PixelFormat::YUYV: + p[0] = y0; + p[1] = u; + p[2] = y1; + p[3] = v; + break; + + case PixelFormat::YVYU: + p[0] = y0; + p[1] = v; + p[2] = y1; + p[3] = u; + break; + + case PixelFormat::VYUY: + p[0] = v; + p[1] = y0; + p[2] = u; + p[3] = y1; + break; + + default: + throw std::invalid_argument("invalid pixelformat"); + } +} + +static void draw_y2xx_packed_macropixel(IFramebuffer& buf, unsigned x, unsigned y, + YUV yuv1, YUV yuv2) +{ + const uint32_t macro_size = 4; + uint16_t* p = reinterpret_cast<uint16_t*>(buf.map(0) + buf.stride(0) * y + x * macro_size); + + switch (buf.format()) { + case PixelFormat::Y210: { + // XXX naive expansion to 10 bits, similar to 10-bit funcs in class RGB + uint16_t y0 = yuv1.y << 2; + uint16_t y1 = yuv2.y << 2; + uint16_t cb = ((yuv1.u << 2) + (yuv2.u << 2)) / 2; + uint16_t cr = ((yuv1.v << 2) + (yuv2.v << 2)) / 2; + + // The 10 bits occupy the msb, so we shift left by 16-10 = 6 + write16le(&p[0], y0 << 6); + write16le(&p[1], cb << 6); + write16le(&p[2], y1 << 6); + write16le(&p[3], cr << 6); + break; + } + + case PixelFormat::Y212: { + // XXX naive expansion to 12 bits + uint16_t y0 = yuv1.y << 4; + uint16_t y1 = yuv2.y << 4; + uint16_t cb = ((yuv1.u << 4) + (yuv2.u << 4)) / 2; + uint16_t cr = ((yuv1.v << 4) + (yuv2.v << 4)) / 2; + + // The 10 bits occupy the msb, so we shift left by 16-12 = 4 + write16le(&p[0], y0 << 4); + write16le(&p[1], cb << 4); + write16le(&p[2], y1 << 4); + write16le(&p[3], cr << 4); + break; + } + + case PixelFormat::Y216: { + // XXX naive expansion to 16 bits + uint16_t y0 = yuv1.y << 8; + uint16_t y1 = yuv2.y << 8; + uint16_t cb = ((yuv1.u << 8) + (yuv2.u << 8)) / 2; + uint16_t cr = ((yuv1.v << 8) + (yuv2.v << 8)) / 2; + + write16le(&p[0], y0); + write16le(&p[1], cb); + write16le(&p[2], y1); + write16le(&p[3], cr); + break; + } + + default: + throw std::invalid_argument("invalid pixelformat"); + } +} + +static void draw_yuv422_semiplanar_macropixel(IFramebuffer& buf, unsigned x, unsigned y, + YUV yuv1, YUV yuv2) +{ + uint8_t* py = reinterpret_cast<uint8_t*>(buf.map(0) + buf.stride(0) * y + x); + uint8_t* puv = reinterpret_cast<uint8_t*>(buf.map(1) + buf.stride(1) * y + x); + + uint8_t y0 = yuv1.y; + uint8_t y1 = yuv2.y; + uint8_t u = (yuv1.u + yuv2.u) / 2; + uint8_t v = (yuv1.v + yuv2.v) / 2; + + switch (buf.format()) { + case PixelFormat::NV16: + py[0] = y0; + py[1] = y1; + puv[0] = u; + puv[1] = v; + break; + + case PixelFormat::NV61: + py[0] = y0; + py[1] = y1; + puv[0] = v; + puv[1] = u; + break; + + default: + throw std::invalid_argument("invalid pixelformat"); + } +} + +static void draw_yuv422_planar_macropixel(IFramebuffer& buf, unsigned x, unsigned y, + YUV yuv1, YUV yuv2) +{ + uint8_t* py = reinterpret_cast<uint8_t*>(buf.map(0) + buf.stride(0) * y + x); + uint8_t* pu = reinterpret_cast<uint8_t*>(buf.map(1) + buf.stride(1) * y + x / 2); + uint8_t* pv = reinterpret_cast<uint8_t*>(buf.map(2) + buf.stride(2) * y + x / 2); + + uint8_t y0 = yuv1.y; + uint8_t y1 = yuv2.y; + uint8_t u = (yuv1.u + yuv2.u) / 2; + uint8_t v = (yuv1.v + yuv2.v) / 2; + + switch (buf.format()) { + case PixelFormat::YUV422: + py[0] = y0; + py[1] = y1; + pu[0] = u; + pv[0] = v; + break; + + case PixelFormat::YVU422: + py[0] = y0; + py[1] = y1; + pu[0] = v; + pv[0] = u; + break; + + default: + throw std::invalid_argument("invalid pixelformat"); + } +} + +void draw_yuv422_macropixel(IFramebuffer& buf, unsigned x, unsigned y, YUV yuv1, YUV yuv2) +{ + if ((x + 1) >= buf.width() || y >= buf.height()) + throw runtime_error("attempt to draw outside the buffer"); + + ASSERT((x & 1) == 0); + + switch (buf.format()) { + case PixelFormat::UYVY: + case PixelFormat::YUYV: + case PixelFormat::YVYU: + case PixelFormat::VYUY: + draw_yuv422_packed_macropixel(buf, x, y, yuv1, yuv2); + break; + + case PixelFormat::Y210: + case PixelFormat::Y212: + case PixelFormat::Y216: + draw_y2xx_packed_macropixel(buf, x, y, yuv1, yuv2); + break; + + case PixelFormat::NV16: + case PixelFormat::NV61: + draw_yuv422_semiplanar_macropixel(buf, x, y, yuv1, yuv2); + break; + + case PixelFormat::YUV422: + case PixelFormat::YVU422: + draw_yuv422_planar_macropixel(buf, x, y, yuv1, yuv2); + break; + + default: + throw std::invalid_argument("invalid pixelformat"); + } +} + +static void draw_yuv420_semiplanar_macropixel(IFramebuffer& buf, unsigned x, unsigned y, + YUV yuv1, YUV yuv2, YUV yuv3, YUV yuv4) +{ + uint8_t* py1 = reinterpret_cast<uint8_t*>(buf.map(0) + buf.stride(0) * (y + 0) + x); + uint8_t* py2 = reinterpret_cast<uint8_t*>(buf.map(0) + buf.stride(0) * (y + 1) + x); + + uint8_t* puv = reinterpret_cast<uint8_t*>(buf.map(1) + buf.stride(1) * (y / 2) + x); + + uint8_t y0 = yuv1.y; + uint8_t y1 = yuv2.y; + uint8_t y2 = yuv3.y; + uint8_t y3 = yuv4.y; + uint8_t u = (yuv1.u + yuv2.u + yuv3.u + yuv4.u) / 4; + uint8_t v = (yuv1.v + yuv2.v + yuv3.v + yuv4.v) / 4; + + switch (buf.format()) { + case PixelFormat::NV12: + py1[0] = y0; + py1[1] = y1; + py2[0] = y2; + py2[1] = y3; + puv[0] = u; + puv[1] = v; + break; + + case PixelFormat::NV21: + py1[0] = y0; + py1[1] = y1; + py2[0] = y2; + py2[1] = y3; + puv[0] = v; + puv[1] = u; + break; + + default: + throw std::invalid_argument("invalid pixelformat"); + } +} + +static void draw_yuv420_planar_macropixel(IFramebuffer& buf, unsigned x, unsigned y, + YUV yuv1, YUV yuv2, YUV yuv3, YUV yuv4) +{ + uint8_t* py1 = reinterpret_cast<uint8_t*>(buf.map(0) + buf.stride(0) * (y + 0) + x); + uint8_t* py2 = reinterpret_cast<uint8_t*>(buf.map(0) + buf.stride(0) * (y + 1) + x); + + uint8_t* pu = reinterpret_cast<uint8_t*>(buf.map(1) + buf.stride(1) * (y / 2) + x / 2); + uint8_t* pv = reinterpret_cast<uint8_t*>(buf.map(2) + buf.stride(2) * (y / 2) + x / 2); + + uint8_t y0 = yuv1.y; + uint8_t y1 = yuv2.y; + uint8_t y2 = yuv3.y; + uint8_t y3 = yuv4.y; + uint8_t u = (yuv1.u + yuv2.u + yuv3.u + yuv4.u) / 4; + uint8_t v = (yuv1.v + yuv2.v + yuv3.v + yuv4.v) / 4; + + switch (buf.format()) { + case PixelFormat::YUV420: + py1[0] = y0; + py1[1] = y1; + py2[0] = y2; + py2[1] = y3; + pu[0] = u; + pv[0] = v; + break; + + case PixelFormat::YVU420: + py1[0] = y0; + py1[1] = y1; + py2[0] = y2; + py2[1] = y3; + pu[0] = v; + pv[0] = u; + break; + + default: + throw std::invalid_argument("invalid pixelformat"); + } +} + +void draw_yuv420_macropixel(IFramebuffer& buf, unsigned x, unsigned y, + YUV yuv1, YUV yuv2, YUV yuv3, YUV yuv4) +{ + if ((x + 1) >= buf.width() || (y + 1) >= buf.height()) + throw runtime_error("attempt to draw outside the buffer"); + + ASSERT((x & 1) == 0); + ASSERT((y & 1) == 0); + + switch (buf.format()) { + case PixelFormat::NV12: + case PixelFormat::NV21: + draw_yuv420_semiplanar_macropixel(buf, x, y, yuv1, yuv2, yuv3, yuv4); + break; + + case PixelFormat::YUV420: + case PixelFormat::YVU420: + draw_yuv420_planar_macropixel(buf, x, y, yuv1, yuv2, yuv3, yuv4); + break; + + default: + throw std::invalid_argument("invalid pixelformat"); + } +} + +void draw_rect(IFramebuffer& fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, RGB color) +{ + unsigned i, j; + YUV yuvcolor = color.yuv(); + + switch (fb.format()) { + case PixelFormat::XRGB8888: + case PixelFormat::XBGR8888: + case PixelFormat::ARGB8888: + case PixelFormat::ABGR8888: + case PixelFormat::RGB888: + case PixelFormat::BGR888: + case PixelFormat::RGB565: + case PixelFormat::BGR565: + case PixelFormat::XRGB4444: + case PixelFormat::XRGB1555: + case PixelFormat::ARGB4444: + case PixelFormat::ARGB1555: + case PixelFormat::RGB332: + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++) { + draw_rgb_pixel(fb, x + i, y + j, color); + } + } + break; + + case PixelFormat::YUV444: + case PixelFormat::YVU444: + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++) { + draw_yuv444_pixel(fb, x + i, y + j, yuvcolor); + } + } + break; + + case PixelFormat::UYVY: + case PixelFormat::YUYV: + case PixelFormat::YVYU: + case PixelFormat::VYUY: + case PixelFormat::NV16: + case PixelFormat::NV61: + case PixelFormat::YUV422: + case PixelFormat::YVU422: + for (j = 0; j < h; j++) { + for (i = 0; i < w; i += 2) { + draw_yuv422_macropixel(fb, x + i, y + j, yuvcolor, yuvcolor); + } + } + break; + + case PixelFormat::NV12: + case PixelFormat::NV21: + case PixelFormat::YUV420: + case PixelFormat::YVU420: + for (j = 0; j < h; j += 2) { + for (i = 0; i < w; i += 2) { + draw_yuv420_macropixel(fb, x + i, y + j, + yuvcolor, yuvcolor, yuvcolor, yuvcolor); + } + } + break; + default: + throw std::invalid_argument("draw_rect: unknown pixelformat"); + } +} + +void draw_horiz_line(IFramebuffer& fb, uint32_t x1, uint32_t x2, uint32_t y, RGB color) +{ + for (uint32_t x = x1; x <= x2; ++x) + draw_rgb_pixel(fb, x, y, color); +} + +void draw_circle(IFramebuffer& fb, int32_t xCenter, int32_t yCenter, int32_t radius, RGB color) +{ + int32_t r2 = radius * radius; + + for (int y = -radius; y <= radius; y++) { + int32_t x = (int)(sqrt(r2 - y * y) + 0.5); + draw_horiz_line(fb, xCenter - x, xCenter + x, yCenter - y, color); + } +} + +static bool get_char_pixel(char c, uint32_t x, uint32_t y) +{ +#include "font_8x8.h" + + uint8_t bits = fontdata_8x8[8 * c + y]; + bool bit = (bits >> (7 - x)) & 1; + + return bit; +} + +static void draw_char(IFramebuffer& buf, uint32_t xpos, uint32_t ypos, char c, RGB color) +{ + // Skip characters that are not fully drawable + if (xpos + 8 > buf.width() || ypos + 8 > buf.height()) + return; + + unsigned x, y; + YUV yuvcolor = color.yuv(); + + switch (buf.format()) { + case PixelFormat::XRGB8888: + case PixelFormat::XBGR8888: + case PixelFormat::ARGB8888: + case PixelFormat::ABGR8888: + case PixelFormat::RGB888: + case PixelFormat::BGR888: + case PixelFormat::RGB565: + case PixelFormat::BGR565: + case PixelFormat::XRGB4444: + case PixelFormat::XRGB1555: + case PixelFormat::ARGB4444: + case PixelFormat::ARGB1555: + case PixelFormat::RGB332: + for (y = 0; y < 8; y++) { + for (x = 0; x < 8; x++) { + bool b = get_char_pixel(c, x, y); + + draw_rgb_pixel(buf, xpos + x, ypos + y, b ? color : RGB()); + } + } + break; + + case PixelFormat::YUV444: + case PixelFormat::YVU444: + for (y = 0; y < 8; y++) { + for (x = 0; x < 8; x++) { + bool b = get_char_pixel(c, x, y); + + draw_yuv444_pixel(buf, xpos + x, ypos + y, b ? yuvcolor : YUV(RGB())); + } + } + break; + + case PixelFormat::UYVY: + case PixelFormat::YUYV: + case PixelFormat::YVYU: + case PixelFormat::VYUY: + case PixelFormat::NV16: + case PixelFormat::NV61: + case PixelFormat::YUV422: + case PixelFormat::YVU422: + for (y = 0; y < 8; y++) { + for (x = 0; x < 8; x += 2) { + bool b0 = get_char_pixel(c, x, y); + bool b1 = get_char_pixel(c, x + 1, y); + + draw_yuv422_macropixel(buf, xpos + x, ypos + y, + b0 ? yuvcolor : YUV(RGB()), b1 ? yuvcolor : YUV(RGB())); + } + } + break; + + case PixelFormat::NV12: + case PixelFormat::NV21: + case PixelFormat::YUV420: + case PixelFormat::YVU420: + for (y = 0; y < 8; y += 2) { + for (x = 0; x < 8; x += 2) { + bool b00 = get_char_pixel(c, x, y); + bool b10 = get_char_pixel(c, x + 1, y); + bool b01 = get_char_pixel(c, x, y + 1); + bool b11 = get_char_pixel(c, x + 1, y + 1); + + draw_yuv420_macropixel(buf, xpos + x, ypos + y, + b00 ? yuvcolor : YUV(RGB()), b10 ? yuvcolor : YUV(RGB()), + b01 ? yuvcolor : YUV(RGB()), b11 ? yuvcolor : YUV(RGB())); + } + } + break; + default: + throw std::invalid_argument("draw_char: unknown pixelformat"); + } +} + +void draw_text(IFramebuffer& buf, uint32_t x, uint32_t y, const string& str, RGB color) +{ + for (unsigned i = 0; i < str.size(); i++) + draw_char(buf, (x + 8 * i), y, str[i], color); +} + +} // namespace kms diff --git a/kms++util/src/extcpuframebuffer.cpp b/kms++util/src/extcpuframebuffer.cpp new file mode 100644 index 0000000..0ee79ee --- /dev/null +++ b/kms++util/src/extcpuframebuffer.cpp @@ -0,0 +1,48 @@ + +#include <kms++util/kms++util.h> + +using namespace std; + +namespace kms +{ +ExtCPUFramebuffer::ExtCPUFramebuffer(uint32_t width, uint32_t height, PixelFormat format, + uint8_t* buffer, uint32_t size, uint32_t pitch, uint32_t offset) + : m_width(width), m_height(height), m_format(format) +{ + const PixelFormatInfo& format_info = get_pixel_format_info(m_format); + + m_num_planes = format_info.num_planes; + + ASSERT(m_num_planes == 1); + + FramebufferPlane& plane = m_planes[0]; + + plane.stride = pitch; + plane.size = size; + plane.offset = offset; + plane.map = buffer; +} + +ExtCPUFramebuffer::ExtCPUFramebuffer(uint32_t width, uint32_t height, PixelFormat format, + uint8_t* buffers[4], uint32_t sizes[4], uint32_t pitches[4], uint32_t offsets[4]) + : m_width(width), m_height(height), m_format(format) +{ + const PixelFormatInfo& format_info = get_pixel_format_info(m_format); + + m_num_planes = format_info.num_planes; + + for (unsigned i = 0; i < format_info.num_planes; ++i) { + FramebufferPlane& plane = m_planes[i]; + + plane.stride = pitches[i]; + plane.size = sizes[i]; + plane.offset = offsets[i]; + plane.map = buffers[i]; + } +} + +ExtCPUFramebuffer::~ExtCPUFramebuffer() +{ +} + +} // namespace kms diff --git a/kms++util/src/font_8x8.h b/kms++util/src/font_8x8.h new file mode 100644 index 0000000..ea01ecb --- /dev/null +++ b/kms++util/src/font_8x8.h @@ -0,0 +1,2570 @@ +/**********************************************/ +/* */ +/* Font file generated by cpi2fnt */ +/* */ +/**********************************************/ + +#include <stdint.h> + +const uint8_t fontdata_8x8[] = { + + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 1 0x01 '^A' */ + 0x7e, /* 01111110 */ + 0x81, /* 10000001 */ + 0xa5, /* 10100101 */ + 0x81, /* 10000001 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0x81, /* 10000001 */ + 0x7e, /* 01111110 */ + + /* 2 0x02 '^B' */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xdb, /* 11011011 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + + /* 3 0x03 '^C' */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + + /* 4 0x04 '^D' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + + /* 5 0x05 '^E' */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + + /* 6 0x06 '^F' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + + /* 7 0x07 '^G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 8 0x08 '^H' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xe7, /* 11100111 */ + 0xc3, /* 11000011 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 9 0x09 '^I' */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x42, /* 01000010 */ + 0x42, /* 01000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 10 0x0a '^J' */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0x99, /* 10011001 */ + 0xbd, /* 10111101 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0xc3, /* 11000011 */ + 0xff, /* 11111111 */ + + /* 11 0x0b '^K' */ + 0x0f, /* 00001111 */ + 0x07, /* 00000111 */ + 0x0f, /* 00001111 */ + 0x7d, /* 01111101 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + + /* 12 0x0c '^L' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + + /* 13 0x0d '^M' */ + 0x3f, /* 00111111 */ + 0x33, /* 00110011 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x70, /* 01110000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + + /* 14 0x0e '^N' */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x67, /* 01100111 */ + 0xe6, /* 11100110 */ + 0xc0, /* 11000000 */ + + /* 15 0x0f '^O' */ + 0x18, /* 00011000 */ + 0xdb, /* 11011011 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0x3c, /* 00111100 */ + 0xdb, /* 11011011 */ + 0x18, /* 00011000 */ + + /* 16 0x10 '^P' */ + 0x80, /* 10000000 */ + 0xe0, /* 11100000 */ + 0xf8, /* 11111000 */ + 0xfe, /* 11111110 */ + 0xf8, /* 11111000 */ + 0xe0, /* 11100000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + + /* 17 0x11 '^Q' */ + 0x02, /* 00000010 */ + 0x0e, /* 00001110 */ + 0x3e, /* 00111110 */ + 0xfe, /* 11111110 */ + 0x3e, /* 00111110 */ + 0x0e, /* 00001110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + + /* 18 0x12 '^R' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + + /* 19 0x13 '^S' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* 20 0x14 '^T' */ + 0x7f, /* 01111111 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7b, /* 01111011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x00, /* 00000000 */ + + /* 21 0x15 '^U' */ + 0x3e, /* 00111110 */ + 0x61, /* 01100001 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x86, /* 10000110 */ + 0x7c, /* 01111100 */ + + /* 22 0x16 '^V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 23 0x17 '^W' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + + /* 24 0x18 '^X' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 25 0x19 '^Y' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 26 0x1a '^Z' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 27 0x1b '^[' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xfe, /* 11111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 28 0x1c '^\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 29 0x1d '^]' */ + 0x00, /* 00000000 */ + 0x24, /* 00100100 */ + 0x66, /* 01100110 */ + 0xff, /* 11111111 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 30 0x1e '^^' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 31 0x1f '^_' */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 32 0x20 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 33 0x21 '!' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 34 0x22 '"' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 35 0x23 '#' */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 36 0x24 '$' */ + 0x18, /* 00011000 */ + 0x3e, /* 00111110 */ + 0x60, /* 01100000 */ + 0x3c, /* 00111100 */ + 0x06, /* 00000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 37 0x25 '%' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x66, /* 01100110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 38 0x26 '&' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 39 0x27 ''' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 40 0x28 '(' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + + /* 41 0x29 ')' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + + /* 42 0x2a '*' */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0xff, /* 11111111 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 43 0x2b '+' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 44 0x2c ',' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + + /* 45 0x2d '-' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 46 0x2e '.' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 47 0x2f '/' */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + + /* 48 0x30 '0' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 49 0x31 '1' */ + 0x18, /* 00011000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 50 0x32 '2' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x1c, /* 00011100 */ + 0x30, /* 00110000 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 51 0x33 '3' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x3c, /* 00111100 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 52 0x34 '4' */ + 0x1c, /* 00011100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + + /* 53 0x35 '5' */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 54 0x36 '6' */ + 0x38, /* 00111000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 55 0x37 '7' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + + /* 56 0x38 '8' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 57 0x39 '9' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 58 0x3a ':' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 59 0x3b ';' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + + /* 60 0x3c '<' */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + + /* 61 0x3d '=' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 62 0x3e '>' */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + + /* 63 0x3f '?' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 64 0x40 '@' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xc0, /* 11000000 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 65 0x41 'A' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 66 0x42 'B' */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 67 0x43 'C' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 68 0x44 'D' */ + 0xf8, /* 11111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + + /* 69 0x45 'E' */ + 0xfe, /* 11111110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x62, /* 01100010 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 70 0x46 'F' */ + 0xfe, /* 11111110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 71 0x47 'G' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xce, /* 11001110 */ + 0x66, /* 01100110 */ + 0x3a, /* 00111010 */ + 0x00, /* 00000000 */ + + /* 72 0x48 'H' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 73 0x49 'I' */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 74 0x4a 'J' */ + 0x1e, /* 00011110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 75 0x4b 'K' */ + 0xe6, /* 11100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 76 0x4c 'L' */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 77 0x4d 'M' */ + 0xc6, /* 11000110 */ + 0xee, /* 11101110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 78 0x4e 'N' */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 79 0x4f 'O' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 80 0x50 'P' */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 81 0x51 'Q' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xce, /* 11001110 */ + 0x7c, /* 01111100 */ + 0x0e, /* 00001110 */ + + /* 82 0x52 'R' */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 83 0x53 'S' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 84 0x54 'T' */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x5a, /* 01011010 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 85 0x55 'U' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 86 0x56 'V' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 87 0x57 'W' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 88 0x58 'X' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 89 0x59 'Y' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 90 0x5a 'Z' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x8c, /* 10001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 91 0x5b '[' */ + 0x3c, /* 00111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 92 0x5c '\' */ + 0xc0, /* 11000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + + /* 93 0x5d ']' */ + 0x3c, /* 00111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 94 0x5e '^' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 95 0x5f '_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + + /* 96 0x60 '`' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 97 0x61 'a' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 98 0x62 'b' */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + + /* 99 0x63 'c' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 100 0x64 'd' */ + 0x1c, /* 00011100 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 101 0x65 'e' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 102 0x66 'f' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x60, /* 01100000 */ + 0xf8, /* 11111000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 103 0x67 'g' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0xf8, /* 11111000 */ + + /* 104 0x68 'h' */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x6c, /* 01101100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 105 0x69 'i' */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 106 0x6a 'j' */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + + /* 107 0x6b 'k' */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 108 0x6c 'l' */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 109 0x6d 'm' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0x00, /* 00000000 */ + + /* 110 0x6e 'n' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* 111 0x6f 'o' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 112 0x70 'p' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + + /* 113 0x71 'q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + + /* 114 0x72 'r' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 115 0x73 's' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 116 0x74 't' */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0xfc, /* 11111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x36, /* 00110110 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + + /* 117 0x75 'u' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 118 0x76 'v' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 119 0x77 'w' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 120 0x78 'x' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 121 0x79 'y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + + /* 122 0x7a 'z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x4c, /* 01001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 123 0x7b '{' */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + + /* 124 0x7c '|' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 125 0x7d '}' */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* 126 0x7e '~' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 127 0x7f '' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 128 0x80 '€' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + + /* 129 0x81 '' */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 130 0x82 '‚' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 131 0x83 'ƒ' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 132 0x84 '„' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 133 0x85 '…' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 134 0x86 '†' */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 135 0x87 '‡' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x7e, /* 01111110 */ + 0x0c, /* 00001100 */ + 0x38, /* 00111000 */ + + /* 136 0x88 'ˆ' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 137 0x89 '‰' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 138 0x8a 'Š' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 139 0x8b '‹' */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 140 0x8c 'Œ' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 141 0x8d '' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 142 0x8e 'Ž' */ + 0xc6, /* 11000110 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 143 0x8f '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 144 0x90 '' */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xf8, /* 11111000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 145 0x91 '‘' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 146 0x92 '’' */ + 0x3e, /* 00111110 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + + /* 147 0x93 '“' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 148 0x94 '”' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 149 0x95 '•' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 150 0x96 '–' */ + 0x78, /* 01111000 */ + 0x84, /* 10000100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 151 0x97 '—' */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 152 0x98 '˜' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + + /* 153 0x99 '™' */ + 0xc6, /* 11000110 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 154 0x9a 'š' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 155 0x9b '›' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 156 0x9c 'œ' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x64, /* 01100100 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 157 0x9d '' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 158 0x9e 'ž' */ + 0xf8, /* 11111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xfa, /* 11111010 */ + 0xc6, /* 11000110 */ + 0xcf, /* 11001111 */ + 0xc6, /* 11000110 */ + 0xc7, /* 11000111 */ + + /* 159 0x9f 'Ÿ' */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* 160 0xa0 ' ' */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 161 0xa1 '¡' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 162 0xa2 '¢' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 163 0xa3 '£' */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 164 0xa4 '¤' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* 165 0xa5 '¥' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + + /* 166 0xa6 '¦' */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 167 0xa7 '§' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 168 0xa8 '¨' */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x63, /* 01100011 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + + /* 169 0xa9 '©' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 170 0xaa 'ª' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 171 0xab '«' */ + 0x63, /* 01100011 */ + 0xe6, /* 11100110 */ + 0x6c, /* 01101100 */ + 0x7e, /* 01111110 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x0f, /* 00001111 */ + + /* 172 0xac '¬' */ + 0x63, /* 01100011 */ + 0xe6, /* 11100110 */ + 0x6c, /* 01101100 */ + 0x7a, /* 01111010 */ + 0x36, /* 00110110 */ + 0x6a, /* 01101010 */ + 0xdf, /* 11011111 */ + 0x06, /* 00000110 */ + + /* 173 0xad '' */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 174 0xae '®' */ + 0x00, /* 00000000 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x66, /* 01100110 */ + 0x33, /* 00110011 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 175 0xaf '¯' */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x66, /* 01100110 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 176 0xb0 '°' */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + + /* 177 0xb1 '±' */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + + /* 178 0xb2 '²' */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + + /* 179 0xb3 '³' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 180 0xb4 '´' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 181 0xb5 'µ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 182 0xb6 '¶' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 183 0xb7 '·' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 184 0xb8 '¸' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 185 0xb9 '¹' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 186 0xba 'º' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 187 0xbb '»' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 188 0xbc '¼' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 189 0xbd '½' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 190 0xbe '¾' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 191 0xbf '¿' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 192 0xc0 'À' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 193 0xc1 'Á' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 194 0xc2 'Â' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 195 0xc3 'Ã' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 196 0xc4 'Ä' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 197 0xc5 'Å' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 198 0xc6 'Æ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 199 0xc7 'Ç' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 200 0xc8 'È' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 201 0xc9 'É' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 202 0xca 'Ê' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 203 0xcb 'Ë' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 204 0xcc 'Ì' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 205 0xcd 'Í' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 206 0xce 'Î' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 207 0xcf 'Ï' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 208 0xd0 'Ð' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 209 0xd1 'Ñ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 210 0xd2 'Ò' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 211 0xd3 'Ó' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 212 0xd4 'Ô' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 213 0xd5 'Õ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 214 0xd6 'Ö' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 215 0xd7 '×' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 216 0xd8 'Ø' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 217 0xd9 'Ù' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 218 0xda 'Ú' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 219 0xdb 'Û' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 220 0xdc 'Ü' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 221 0xdd 'Ý' */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + + /* 222 0xde 'Þ' */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + + /* 223 0xdf 'ß' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 224 0xe0 'à' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xc8, /* 11001000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 225 0xe1 'á' */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xd8, /* 11011000 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + + /* 226 0xe2 'â' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + + /* 227 0xe3 'ã' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 228 0xe4 'ä' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 229 0xe5 'å' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* 230 0xe6 'æ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0xc0, /* 11000000 */ + + /* 231 0xe7 'ç' */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 232 0xe8 'è' */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + + /* 233 0xe9 'é' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 234 0xea 'ê' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xee, /* 11101110 */ + 0x00, /* 00000000 */ + + /* 235 0xeb 'ë' */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x3e, /* 00111110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 236 0xec 'ì' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 237 0xed 'í' */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + + /* 238 0xee 'î' */ + 0x1e, /* 00011110 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + + /* 239 0xef 'ï' */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 240 0xf0 'ð' */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 241 0xf1 'ñ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 242 0xf2 'ò' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 243 0xf3 'ó' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 244 0xf4 'ô' */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 245 0xf5 'õ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + + /* 246 0xf6 'ö' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 247 0xf7 '÷' */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 248 0xf8 'ø' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 249 0xf9 'ù' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 250 0xfa 'ú' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 251 0xfb 'û' */ + 0x0f, /* 00001111 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xec, /* 11101100 */ + 0x6c, /* 01101100 */ + 0x3c, /* 00111100 */ + 0x1c, /* 00011100 */ + + /* 252 0xfc 'ü' */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 253 0xfd 'ý' */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 254 0xfe 'þ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 255 0xff 'ÿ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ +}; diff --git a/kms++util/src/opts.cpp b/kms++util/src/opts.cpp new file mode 100644 index 0000000..ba49995 --- /dev/null +++ b/kms++util/src/opts.cpp @@ -0,0 +1,118 @@ +#include <algorithm> +#include <stdexcept> + +#include <unistd.h> +#include <getopt.h> + +#include <kms++util/opts.h> + +using namespace std; + +Option::Option(const string& str, function<void()> func) + : m_void_func(func) +{ + parse(str); +} + +Option::Option(const string& str, function<void(const string)> func) + : m_func(func) +{ + parse(str); +} + +void Option::parse(const string& str) +{ + auto iend = str.end(); + if (*(iend - 1) == '=') { + iend--; + m_has_arg = 1; + } else if (*(iend - 1) == '?') { + iend--; + m_has_arg = 2; + } else { + m_has_arg = 0; + } + + auto isplit = find(str.begin(), iend, '|'); + + if (isplit != str.begin()) + m_short = str[0]; + else + m_short = 0; + + if (isplit != iend) + m_long = string(isplit + 1, iend); +} + +OptionSet::OptionSet(initializer_list<Option> il) + : m_opts(il) +{ +} + +void OptionSet::parse(int argc, char** argv) +{ + string shortopts = ":"; + vector<struct option> longopts; + + for (unsigned opt_idx = 0; opt_idx < m_opts.size(); ++opt_idx) { + const Option& o = m_opts[opt_idx]; + + if (o.m_short != 0) { + shortopts.push_back(o.m_short); + if (o.m_has_arg == 1) + shortopts.push_back(':'); + else if (o.m_has_arg == 2) + shortopts.append("::"); + } + + if (!o.m_long.empty()) { + struct option copt; + copt.name = o.m_long.c_str(); + copt.has_arg = o.m_has_arg; + copt.flag = 0; + copt.val = opt_idx + 1000; + longopts.push_back(copt); + } + } + + longopts.push_back(option{}); + + while (1) { + int long_idx = 0; + int c = getopt_long(argc, argv, shortopts.c_str(), + longopts.data(), &long_idx); + if (c == -1) + break; + + if (c == '?') + throw std::invalid_argument(string("Unrecognized option ") + argv[optind - 1]); + + if (c == ':') { + const Option& o = find_opt(optopt); + if (optopt < 256) + throw std::invalid_argument(string("Missing argument to -") + o.m_short); + else + throw std::invalid_argument(string("Missing argument to --") + o.m_long); + } + + string sarg = { optarg ?: "" }; + + const Option& opt = find_opt(c); + + if (opt.m_func) + opt.m_func(sarg); + else + opt.m_void_func(); + } + + for (int i = optind; i < argc; ++i) + m_params.push_back(argv[i]); +} + +const Option& OptionSet::find_opt(int c) +{ + if (c < 256) + return *find_if(m_opts.begin(), m_opts.end(), [c](const Option& o) { return o.m_short == c; }); + else + return m_opts[c - 1000]; +} diff --git a/kms++util/src/resourcemanager.cpp b/kms++util/src/resourcemanager.cpp new file mode 100644 index 0000000..424c8f0 --- /dev/null +++ b/kms++util/src/resourcemanager.cpp @@ -0,0 +1,221 @@ +#include <kms++util/resourcemanager.h> +#include <algorithm> +#include <kms++util/strhelpers.h> + +using namespace kms; +using namespace std; + +ResourceManager::ResourceManager(Card& card) + : m_card(card) +{ +} + +void ResourceManager::reset() +{ + m_reserved_connectors.clear(); + m_reserved_crtcs.clear(); + m_reserved_planes.clear(); +} + +static Connector* find_connector(Card& card, const set<Connector*>& reserved) +{ + for (Connector* conn : card.get_connectors()) { + if (!conn->connected()) + continue; + + if (reserved.count(conn)) + continue; + + return conn; + } + + return nullptr; +} + +static Connector* resolve_connector(Card& card, const string& name, const set<Connector*>& reserved) +{ + auto connectors = card.get_connectors(); + + if (name[0] == '@') { + char* endptr; + unsigned id = strtoul(name.c_str() + 1, &endptr, 10); + if (*endptr == 0) { + Connector* c = card.get_connector(id); + + if (!c || reserved.count(c)) + return nullptr; + + return c; + } + } else { + char* endptr; + unsigned idx = strtoul(name.c_str(), &endptr, 10); + if (*endptr == 0) { + if (idx >= connectors.size()) + return nullptr; + + Connector* c = connectors[idx]; + + if (reserved.count(c)) + return nullptr; + + return c; + } + } + + for (Connector* conn : connectors) { + if (to_lower(conn->fullname()).find(to_lower(name)) == string::npos) + continue; + + if (reserved.count(conn)) + continue; + + return conn; + } + + return nullptr; +} + +Connector* ResourceManager::reserve_connector(const string& name) +{ + Connector* conn; + + if (name.empty()) + conn = find_connector(m_card, m_reserved_connectors); + else + conn = resolve_connector(m_card, name, m_reserved_connectors); + + if (!conn) + return nullptr; + + m_reserved_connectors.insert(conn); + return conn; +} + +Connector* ResourceManager::reserve_connector(Connector* conn) +{ + if (!conn) + return nullptr; + + if (m_reserved_connectors.count(conn)) + return nullptr; + + m_reserved_connectors.insert(conn); + return conn; +} + +void ResourceManager::release_connector(Connector* conn) +{ + m_reserved_connectors.erase(conn); +} + +Crtc* ResourceManager::reserve_crtc(Connector* conn) +{ + if (!conn) + return nullptr; + + if (Crtc* crtc = conn->get_current_crtc()) { + m_reserved_crtcs.insert(crtc); + return crtc; + } + + for (Crtc* crtc : conn->get_possible_crtcs()) { + if (m_reserved_crtcs.count(crtc)) + continue; + + m_reserved_crtcs.insert(crtc); + return crtc; + } + + return nullptr; +} + +Crtc* ResourceManager::reserve_crtc(Crtc* crtc) +{ + if (!crtc) + return nullptr; + + if (m_reserved_crtcs.count(crtc)) + return nullptr; + + m_reserved_crtcs.insert(crtc); + + return crtc; +} + +void ResourceManager::release_crtc(Crtc* crtc) +{ + m_reserved_crtcs.erase(crtc); +} + +Plane* ResourceManager::reserve_plane(Crtc* crtc, PlaneType type, PixelFormat format) +{ + if (!crtc) + return nullptr; + + for (Plane* plane : crtc->get_possible_planes()) { + if (plane->plane_type() != type) + continue; + + if (format != PixelFormat::Undefined && !plane->supports_format(format)) + continue; + + if (m_reserved_planes.count(plane)) + continue; + + m_reserved_planes.insert(plane); + return plane; + } + + return nullptr; +} + +Plane* ResourceManager::reserve_plane(Plane* plane) +{ + if (!plane) + return nullptr; + + if (m_reserved_planes.count(plane)) + return nullptr; + + m_reserved_planes.insert(plane); + + return plane; +} + +Plane* ResourceManager::reserve_generic_plane(Crtc* crtc, PixelFormat format) +{ + if (!crtc) + return nullptr; + + for (Plane* plane : crtc->get_possible_planes()) { + if (plane->plane_type() == PlaneType::Cursor) + continue; + + if (format != PixelFormat::Undefined && !plane->supports_format(format)) + continue; + + if (m_reserved_planes.count(plane)) + continue; + + m_reserved_planes.insert(plane); + return plane; + } + + return nullptr; +} + +Plane* ResourceManager::reserve_primary_plane(Crtc* crtc, PixelFormat format) +{ + return reserve_plane(crtc, PlaneType::Primary, format); +} + +Plane* ResourceManager::reserve_overlay_plane(Crtc* crtc, PixelFormat format) +{ + return reserve_plane(crtc, PlaneType::Overlay, format); +} + +void ResourceManager::release_plane(Plane* plane) +{ + m_reserved_planes.erase(plane); +} diff --git a/kms++util/src/strhelpers.cpp b/kms++util/src/strhelpers.cpp new file mode 100644 index 0000000..5eba2a9 --- /dev/null +++ b/kms++util/src/strhelpers.cpp @@ -0,0 +1,13 @@ +#include <kms++util/strhelpers.h> + +#include <algorithm> +#include <stdarg.h> + +using namespace std; + +string to_lower(const string& str) +{ + string data = str; + transform(data.begin(), data.end(), data.begin(), ::tolower); + return data; +} diff --git a/kms++util/src/testpat.cpp b/kms++util/src/testpat.cpp new file mode 100644 index 0000000..b9f1e56 --- /dev/null +++ b/kms++util/src/testpat.cpp @@ -0,0 +1,577 @@ + +#include <cstring> +#include <functional> +#include <optional> +#include <span> +#include <stdexcept> +#include <vector> + +#ifdef HAS_PTHREAD +#include <thread> +#endif + +#include <kms++/kms++.h> +#include <kms++util/kms++util.h> + +#include "conv.h" + +using namespace std; + +namespace kms +{ +static RGB16 get_test_pattern_pixel_16(size_t w, size_t h, size_t x, size_t y) +{ + const unsigned mw = 20; + + const unsigned xm1 = mw; + const unsigned xm2 = w - mw - 1; + const unsigned ym1 = mw; + const unsigned ym2 = h - mw - 1; + + // white margin lines + if (x == xm1 || x == xm2 || y == ym1 || y == ym2) + return RGB16::from_8(255, 255, 255); + // white box in top left corner + else if (x < xm1 && y < ym1) + return RGB16::from_8(255, 255, 255); + // white box outlines to corners + else if ((x == 0 || x == w - 1) && (y < ym1 || y > ym2)) + return RGB16::from_8(255, 255, 255); + // white box outlines to corners + else if ((y == 0 || y == h - 1) && (x < xm1 || x > xm2)) + return RGB16::from_8(255, 255, 255); + // blue bar on the left + else if (x < xm1 && (y > ym1 && y < ym2)) + return RGB16::from_8(0, 0, 255); + // blue bar on the top + else if (y < ym1 && (x > xm1 && x < xm2)) + return RGB16::from_8(0, 0, 255); + // red bar on the right + else if (x > xm2 && (y > ym1 && y < ym2)) + return RGB16::from_8(255, 0, 0); + // red bar on the bottom + else if (y > ym2 && (x > xm1 && x < xm2)) + return RGB16::from_8(255, 0, 0); + // inside the margins + else if (x > xm1 && x < xm2 && y > ym1 && y < ym2) { + // diagonal line + if (x == y || w - x == h - y) + return RGB16::from_8(255, 255, 255); + // diagonal line + else if (w - x - 1 == y || x == h - y - 1) + return RGB16::from_8(255, 255, 255); + else { + int t = (x - xm1 - 1) * 8 / (xm2 - xm1 - 1); + unsigned r = 0, g = 0, b = 0; + + unsigned c = (y - ym1 - 1) % 256; + + switch (t) { + case 0: + r = c; + break; + case 1: + g = c; + break; + case 2: + b = c; + break; + case 3: + g = b = c; + break; + case 4: + r = b = c; + break; + case 5: + r = g = c; + break; + case 6: + r = g = b = c; + break; + case 7: + break; + } + + return RGB16::from_8(r, g, b); + } + } else { + // black corners + return RGB16::from_8(0, 0, 0); + } +} + +static void get_test_pattern_line(size_t w, size_t h, size_t row, std::span<RGB16> buf) +{ + for (size_t x = 0; x < w; ++x) + buf[x] = get_test_pattern_pixel_16(w, h, x, row); +} + +static void get_test_pattern_line_yuv(size_t w, size_t h, size_t row, + std::span<YUV16> buf, + const TestPatternOptions& options) +{ + for (size_t x = 0; x < w; ++x) + buf[x] = get_test_pattern_pixel_16(w, h, x, row) + .to_yuv(options.rec, options.range); +} + +// SMPTE RP 219-1:2014 +// High-Definition, Standard-Definition Compatible Color Bar Signal +// Limited range YUV +static YUV16 get_smpte_pixel(size_t w, size_t h, size_t x, size_t y) +{ + // Pattern colors (12-bit values, limited range) + constexpr YUV16 gray40 = YUV16::from_12(1658, 2048, 2048); // 40% Gray + constexpr YUV16 white75 = YUV16::from_12(2884, 2048, 2048); // 75% White + constexpr YUV16 yellow75 = YUV16::from_12(2694, 704, 2171); // 75% Yellow + constexpr YUV16 cyan75 = YUV16::from_12(2325, 2356, 704); // 75% Cyan + constexpr YUV16 green75 = YUV16::from_12(2136, 1012, 827); // 75% Green + constexpr YUV16 magenta75 = YUV16::from_12(1004, 3084, 3269); // 75% Magenta + constexpr YUV16 red75 = YUV16::from_12(815, 1740, 3392); // 75% Red + constexpr YUV16 blue75 = YUV16::from_12(446, 3392, 1925); // 75% Blue + constexpr YUV16 cyan100 = YUV16::from_12(3015, 2459, 256); // 100% Cyan + constexpr YUV16 blue100 = YUV16::from_12(509, 3840, 1884); // 100% Blue + constexpr YUV16 yellow100 = YUV16::from_12(3507, 256, 2212); // 100% Yellow + constexpr YUV16 black = YUV16::from_12(256, 2048, 2048); // 0% Black + constexpr YUV16 white100 = YUV16::from_12(3760, 2048, 2048); // 100% White + constexpr YUV16 red100 = YUV16::from_12(1001, 1637, 3840); // 100% Red + constexpr YUV16 gray15 = YUV16::from_12(782, 2048, 2048); // 15% Gray + + // PLUGE steps + constexpr YUV16 black_m2 = YUV16::from_12(186, 2048, 2048); // -2% Black + constexpr YUV16 black_p2 = YUV16::from_12(326, 2048, 2048); // +2% Black + constexpr YUV16 black_p4 = YUV16::from_12(396, 2048, 2048); // +4% Black + + // High precision for x-axis calculations + constexpr size_t M = 1024; + x = x * M; + const size_t a = w * M; + const size_t c = (a * 3 / 4) / 7; + const size_t d = a / 8; + + // Pattern heights + const size_t pattern1_height = (h * 7) / 12; + const size_t pattern2_height = pattern1_height + (h / 12); + const size_t pattern3_height = pattern2_height + (h / 12); + + // Pattern 1 (75% color bars) + if (y < pattern1_height) { + if (x < d || x >= (a - d)) + return gray40; + + size_t bar_index = (x - d) / c; + switch (bar_index) { + case 0: + return white75; + case 1: + return yellow75; + case 2: + return cyan75; + case 3: + return green75; + case 4: + return magenta75; + case 5: + return red75; + default: + return blue75; + } + } + + // Pattern 2 (Color difference reference) + if (y >= pattern1_height && y < pattern2_height) { + if (x < d) + return cyan100; + + if (x >= (a - d)) + return blue100; + + return white75; + } + + // Pattern 3 (Ramp) + if (y >= pattern2_height && y < pattern3_height) { + if (x < d) + return yellow100; + + if (x >= (a - d)) + return red100; + + const size_t ramp_width = a - 2 * d; + const size_t ramp_x = x - d; + + uint16_t y_val = 256 + (3760 - 256) * ramp_x / ramp_width; + return YUV16::from_12(y_val, 2048, 2048); + } + + // Pattern 4 (PLUGE) + if (y >= pattern3_height) { + const size_t c0 = d; + const size_t c1 = c0 + c * 3 / 2; + const size_t c2 = c1 + 2 * c; + const size_t c3 = c2 + c * 5 / 6; + + if (x < c0) + return gray15; + + if (x < c1) + return black; + + if (x < c2) + return white100; + + if (x < c3) + return black; + + if (x >= a - d) + return gray15; + + if (x >= a - d - c) + return black; + + const size_t step = (x - c3) / (c / 3); + + switch (step) { + case 0: + return black_m2; // -2% + case 1: + return black; // 0% + case 2: + return black_p2; // +2% + case 3: + return black; // 0% + default: + return black_p4; // +4% + } + } + + return black; +} + +static void get_smpte_line_rgb(size_t w, size_t h, size_t row, std::span<RGB16> buf, + const TestPatternOptions& options) +{ + for (size_t x = 0; x < w; ++x) + buf[x] = get_smpte_pixel(w, h, x, row) + .to_rgb(RecStandard::BT709, ColorRange::Limited); +} + +static void get_smpte_line_yuv(size_t w, size_t h, size_t row, std::span<YUV16> buf, + const TestPatternOptions& options) +{ + for (size_t x = 0; x < w; ++x) + buf[x] = get_smpte_pixel(w, h, x, row); +} + +static void get_plain_line_rgb(size_t w, const RGB16& color, std::span<RGB16> buf) +{ + for (size_t x = 0; x < w; ++x) + buf[x] = color; +} + +static void get_plain_line_yuv(size_t w, const YUV16& color, std::span<YUV16> buf) +{ + for (size_t x = 0; x < w; ++x) + buf[x] = color; +} + +static void draw_test_pattern_part(IFramebuffer& fb, size_t start_y, size_t end_y, + const TestPatternOptions& options) +{ + std::optional<RGB16> solid; + + if (options.pattern == "red") + solid = RGB16(0xffff, 0, 0); + else if (options.pattern == "green") + solid = RGB16(0, 0xffff, 0); + else if (options.pattern == "blue") + solid = RGB16(0, 0, 0xffff); + else if (options.pattern == "white") + solid = RGB16(0xffff, 0xffff, 0xffff); + else if (options.pattern == "black") + solid = RGB16(0, 0, 0); + + std::function<void(size_t y, std::span<RGB16> span)> generate_line_rgb; + std::function<void(size_t y, std::span<YUV16> span)> generate_line_yuv; + + if (solid.has_value()) { + generate_line_rgb = [&fb, rgb = solid.value()](size_t y, + std::span<RGB16> span) { + get_plain_line_rgb(fb.width(), rgb, span); + }; + + generate_line_yuv = [&fb, rgb = solid.value(), + &options](size_t y, std::span<YUV16> span) { + get_plain_line_yuv(fb.width(), + rgb.to_yuv(options.rec, options.range), span); + }; + } else if (options.pattern == "smpte") { + generate_line_rgb = [&fb, &options](size_t y, std::span<RGB16> span) { + get_smpte_line_rgb(fb.width(), fb.height(), y, span, options); + }; + + generate_line_yuv = [&fb, &options](size_t y, std::span<YUV16> span) { + get_smpte_line_yuv(fb.width(), fb.height(), y, span, options); + }; + } else { + generate_line_rgb = [&fb](size_t y, std::span<RGB16> span) { + get_test_pattern_line(fb.width(), fb.height(), y, span); + }; + + generate_line_yuv = [&fb, &options](size_t y, std::span<YUV16> span) { + get_test_pattern_line_yuv(fb.width(), fb.height(), y, span, + options); + }; + } + +#define CASE_ARGB(x) \ + case PixelFormat::x: \ + ARGB_Writer<x##_Layout>::write_pattern(fb, start_y, end_y, \ + generate_line_rgb); \ + break; + +#define CASE_YUV(x) \ + case PixelFormat::x: \ + YUV_Writer<x##_Layout>::write_pattern(fb, start_y, end_y, \ + generate_line_yuv); \ + break; + +#define CASE_YUV_PACKED(x) \ + case PixelFormat::x: \ + YUVPackedWriter<x##_Layout>::write_pattern(fb, start_y, end_y, \ + generate_line_yuv); \ + break; + +#define CASE_YUV_SEMI(x) \ + case PixelFormat::x: \ + YUVSemiPlanarWriter<x##_Layout>::write_pattern(fb, start_y, end_y, \ + generate_line_yuv); \ + break; + +#define CASE_YUV_PLANAR(x) \ + case PixelFormat::x: \ + YUVPlanarWriter<x##_Layout>::write_pattern(fb, start_y, end_y, \ + generate_line_yuv); \ + break; + +#define CASE_Y_ONLY(x) \ + case PixelFormat::x: \ + Y_Writer<x##_Layout>::write_pattern(fb, start_y, end_y, \ + generate_line_yuv); \ + break; + +#define CASE_YUV_PLANAR_PACKED(x) \ + case PixelFormat::x: \ + YUVPlanarPackedWriter<x##_Layout>::write_pattern(fb, start_y, end_y, \ + generate_line_yuv); \ + break; + +#define CASE_RAW(x) \ + case PixelFormat::x: \ + Bayer_Writer<x##_Layout>::write_pattern(fb, start_y, end_y, \ + generate_line_rgb); \ + break; + +#define CASE_RAW_PACKED(x) \ + case PixelFormat::x: \ + BayerPacked_Writer<x##_Layout>::write_pattern(fb, start_y, end_y, \ + generate_line_rgb); \ + break; + + switch (fb.format()) { + CASE_YUV_SEMI(P230); + CASE_YUV_SEMI(P030); + CASE_YUV_SEMI(NV12); + CASE_YUV_SEMI(NV21); + CASE_YUV_SEMI(NV16); + CASE_YUV_SEMI(NV61); + + CASE_ARGB(RGB565); + CASE_ARGB(BGR565); + + CASE_ARGB(XRGB1555); + CASE_ARGB(ARGB1555); + CASE_ARGB(XRGB4444); + CASE_ARGB(ARGB4444); + + CASE_ARGB(RGB888); + CASE_ARGB(BGR888); + + CASE_ARGB(XRGB8888); + CASE_ARGB(ARGB8888); + CASE_ARGB(XBGR8888); + CASE_ARGB(ABGR8888); + CASE_ARGB(RGBX8888); + CASE_ARGB(RGBA8888); + CASE_ARGB(BGRX8888); + CASE_ARGB(BGRA8888); + CASE_ARGB(XRGB2101010); + CASE_ARGB(ARGB2101010); + CASE_ARGB(XBGR2101010); + CASE_ARGB(ABGR2101010); + CASE_ARGB(RGBX1010102); + CASE_ARGB(RGBA1010102); + CASE_ARGB(BGRX1010102); + CASE_ARGB(BGRA1010102); + + CASE_YUV_PACKED(YUYV); + CASE_YUV_PACKED(YVYU); + CASE_YUV_PACKED(UYVY); + CASE_YUV_PACKED(VYUY); + + CASE_YUV(XVUY2101010); + + CASE_YUV_PLANAR(YUV444); + CASE_YUV_PLANAR(YVU444); + CASE_YUV_PLANAR(YUV422); + CASE_YUV_PLANAR(YVU422); + CASE_YUV_PLANAR(YUV420); + CASE_YUV_PLANAR(YVU420); + + CASE_Y_ONLY(Y8); + CASE_Y_ONLY(XYYY2101010); + + CASE_YUV_PLANAR_PACKED(T430); + + CASE_RAW(SRGGB8); + CASE_RAW(SGBRG8); + CASE_RAW(SGRBG8); + CASE_RAW(SBGGR8); + + CASE_RAW(SRGGB10); + CASE_RAW(SGBRG10); + CASE_RAW(SGRBG10); + CASE_RAW(SBGGR10); + + CASE_RAW(SRGGB12); + CASE_RAW(SGBRG12); + CASE_RAW(SGRBG12); + CASE_RAW(SBGGR12); + + CASE_RAW(SRGGB16); + CASE_RAW(SGBRG16); + CASE_RAW(SGRBG16); + CASE_RAW(SBGGR16); + + CASE_RAW_PACKED(SRGGB10P); + CASE_RAW_PACKED(SGBRG10P); + CASE_RAW_PACKED(SGRBG10P); + CASE_RAW_PACKED(SBGGR10P); + + CASE_RAW_PACKED(SRGGB12P); + CASE_RAW_PACKED(SGBRG12P); + CASE_RAW_PACKED(SGRBG12P); + CASE_RAW_PACKED(SBGGR12P); + + default: + break; + } +} + +void draw_test_pattern_multi(IFramebuffer& fb, const TestPatternOptions& options) +{ + const auto& info = get_pixel_format_info(fb.format()); + uint8_t v_sub = 0; + for (size_t p = 0; p < info.num_planes; ++p) + v_sub = max(v_sub, info.planes[p].vsub); + + if (!v_sub || fb.height() % v_sub) + throw invalid_argument("FB height must be divisible with vsub"); + + // Create the mmaps before starting the threads + for (size_t i = 0; i < fb.num_planes(); ++i) + fb.map(i); + + size_t num_threads = thread::hardware_concurrency(); + + size_t part_height = fb.height() / num_threads; + + // round up to v_sub + part_height = (part_height + v_sub - 1) / v_sub * v_sub; + + vector<thread> workers; + + std::vector<std::exception_ptr> errors(num_threads); + + for (size_t n = 0; n < num_threads; ++n) { + size_t start = n * part_height; + size_t end = start + part_height - 1; + + if (n == num_threads - 1) + end = fb.height() - 1; + + workers.push_back(thread([&fb, start, end, &options, &error = errors[n]]() { + try { + draw_test_pattern_part(fb, start, end, options); + } catch (...) { + error = std::current_exception(); + } + })); + } + + for (thread& t : workers) + t.join(); + + auto i = std::find_if(errors.begin(), errors.end(), + [](const auto& e) { return e != nullptr; }); + if (i != errors.end()) + std::rethrow_exception(*i); +} + +void draw_test_pattern_single(IFramebuffer& fb, const TestPatternOptions& options) +{ + draw_test_pattern_part(fb, 0, fb.height() - 1, options); +} + +void draw_test_pattern(IFramebuffer& fb, const TestPatternOptions& options) +{ +#ifdef HAS_PTHREAD + draw_test_pattern_multi(fb, options); +#else + draw_test_pattern_single(fb, options); +#endif +} + +} // namespace kms + +extern "C" { + +int c_draw_test_pattern(struct CDrawTestPatternParameters* params) +{ + using namespace kms; + + try { + auto fmt = find_pixel_format_by_name(params->format_name); + + ExtCPUFramebuffer fb(params->width, + params->height, + fmt, + params->buffers, + params->sizes, + params->pitches, + params->offsets); + + RecStandard rec; + if (params->rec_standard == 0) + rec = RecStandard::BT601; + else if (params->rec_standard == 1) + rec = RecStandard::BT709; + else if (params->rec_standard == 2) + rec = RecStandard::BT2020; + else + return -1; + + TestPatternOptions options; + options.pattern = params->pattern; + options.rec = rec; + options.range = params->full_range ? ColorRange::Full : ColorRange::Limited; + + draw_test_pattern(fb, options); + } catch (const exception&) { + return -1; + } + + return 0; +} + +} |
