summaryrefslogtreecommitdiff
path: root/kms++util/src
diff options
context:
space:
mode:
Diffstat (limited to 'kms++util/src')
-rw-r--r--kms++util/src/color.cpp187
-rw-r--r--kms++util/src/colorbar.cpp139
-rw-r--r--kms++util/src/conv-common.h177
-rw-r--r--kms++util/src/conv-gray.h130
-rw-r--r--kms++util/src/conv-raw-packed.h230
-rw-r--r--kms++util/src/conv-raw.h202
-rw-r--r--kms++util/src/conv-rgb.h299
-rw-r--r--kms++util/src/conv-yuv-packed.h83
-rw-r--r--kms++util/src/conv-yuv-planar-packed.h125
-rw-r--r--kms++util/src/conv-yuv-planar.h214
-rw-r--r--kms++util/src/conv-yuv-semiplanar.h214
-rw-r--r--kms++util/src/conv-yuv.h147
-rw-r--r--kms++util/src/conv.h11
-rw-r--r--kms++util/src/cpuframebuffer.cpp35
-rw-r--r--kms++util/src/drawing.cpp624
-rw-r--r--kms++util/src/extcpuframebuffer.cpp48
-rw-r--r--kms++util/src/font_8x8.h2570
-rw-r--r--kms++util/src/opts.cpp118
-rw-r--r--kms++util/src/resourcemanager.cpp221
-rw-r--r--kms++util/src/strhelpers.cpp13
-rw-r--r--kms++util/src/testpat.cpp577
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;
+}
+
+}