diff options
author | Tomi Valkeinen <tomi.valkeinen@iki.fi> | 2015-09-28 01:13:34 +0300 |
---|---|---|
committer | Tomi Valkeinen <tomi.valkeinen@iki.fi> | 2015-09-28 01:13:34 +0300 |
commit | 009828beac9bfe9c36d336a4de0d297f90aece52 (patch) | |
tree | 3ea6b22f2036dcc0c23c459ce53bfc2ad282e3e7 /libkms++/utils |
Initial version
Diffstat (limited to 'libkms++/utils')
-rw-r--r-- | libkms++/utils/color.cpp | 67 | ||||
-rw-r--r-- | libkms++/utils/color.h | 48 | ||||
-rw-r--r-- | libkms++/utils/conv.cpp | 138 | ||||
-rw-r--r-- | libkms++/utils/conv.h | 8 | ||||
-rw-r--r-- | libkms++/utils/testpat.cpp | 162 | ||||
-rw-r--r-- | libkms++/utils/testpat.h | 6 |
6 files changed, 429 insertions, 0 deletions
diff --git a/libkms++/utils/color.cpp b/libkms++/utils/color.cpp new file mode 100644 index 0000000..b5b9001 --- /dev/null +++ b/libkms++/utils/color.cpp @@ -0,0 +1,67 @@ +#include "color.h" + +namespace kms +{ +RGB::RGB() +{ + r = g = b = a = 0; +} + +RGB::RGB(uint8_t r, uint8_t g, uint8_t b) +{ + this->r = r; + this->g = g; + this->b = b; + this->a = 0; +} + +uint16_t RGB::rgb565() const +{ + uint16_t r = (this->r >> 3) << 11; + uint16_t g = (this->g >> 2) << 5; + uint16_t b = (this->b >> 3) << 0; + return r | g | b; +} + +YUV RGB::yuv() const +{ + return YUV(*this); +} + + +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_601_Y(uint8_t r, uint8_t g, uint8_t b) +{ + return (((66 * r + 129 * g + 25 * b + 128) >> 8) + 16); +} + +static inline uint8_t MAKE_YUV_601_U(uint8_t r, uint8_t g, uint8_t b) +{ + return (((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128); +} + +static inline uint8_t MAKE_YUV_601_V(uint8_t r, uint8_t g, uint8_t b) +{ + return (((112 * r - 94 * g - 18 * b + 128) >> 8) + 128); +} + +YUV::YUV(const RGB& rgb) +{ + this->y = MAKE_YUV_601_Y(rgb.r, rgb.g, rgb.b); + this->u = MAKE_YUV_601_U(rgb.r, rgb.g, rgb.b); + this->v = MAKE_YUV_601_V(rgb.r, rgb.g, rgb.b); + this->a = rgb.a; +} +} diff --git a/libkms++/utils/color.h b/libkms++/utils/color.h new file mode 100644 index 0000000..1db47e8 --- /dev/null +++ b/libkms++/utils/color.h @@ -0,0 +1,48 @@ +#pragma once + +#include <cstdint> + +namespace kms +{ +struct YUV; + +struct RGB +{ + RGB(); + RGB(uint8_t r, uint8_t g, uint8_t b); + + uint16_t rgb565() const; + YUV yuv() const; + + union { + struct + { + uint8_t b; + uint8_t g; + uint8_t r; + uint8_t a; + }; + + uint32_t raw; + }; +}; + +struct YUV +{ + YUV(); + YUV(uint8_t y, uint8_t u, uint8_t v); + YUV(const RGB& rgb); + + union { + struct + { + uint8_t v; + uint8_t u; + uint8_t y; + uint8_t a; + }; + + uint32_t raw; + }; +}; +} diff --git a/libkms++/utils/conv.cpp b/libkms++/utils/conv.cpp new file mode 100644 index 0000000..d439253 --- /dev/null +++ b/libkms++/utils/conv.cpp @@ -0,0 +1,138 @@ +#include <drm_fourcc.h> +#include <stdexcept> + +#include "framebuffer.h" +#include "color.h" +#include "conv.h" + +namespace kms +{ +static RGB read_rgb(const Framebuffer& fb, int x, int y) +{ + uint32_t *pc = (uint32_t *)(fb.map(0) + fb.stride(0) * y); + + uint32_t c = pc[x]; + + return RGB((c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff); +} + +static YUV read_rgb_as_yuv(const Framebuffer& fb, int x, int y) +{ + RGB rgb = read_rgb(fb, x, y); + return YUV(rgb); +} + +static void fb_rgb_to_packed_yuv(Framebuffer& dst_fb, const Framebuffer& src_fb) +{ + unsigned w = src_fb.width(); + unsigned h = src_fb.height(); + + uint8_t *dst = dst_fb.map(0); + + for (unsigned y = 0; y < h; ++y) { + for (unsigned x = 0; x < w; x += 2) { + YUV yuv1 = read_rgb_as_yuv(src_fb, x + 0, y); + YUV yuv2 = read_rgb_as_yuv(src_fb, x + 1, y); + + switch (dst_fb.format()) { + case DRM_FORMAT_UYVY: + dst[x * 2 + 0] = (yuv1.u + yuv2.u) / 2; + dst[x * 2 + 1] = yuv1.y; + dst[x * 2 + 2] = (yuv1.v + yuv2.v) / 2; + dst[x * 2 + 3] = yuv2.y; + break; + case DRM_FORMAT_YUYV: + dst[x * 2 + 0] = yuv1.y; + dst[x * 2 + 1] = (yuv1.u + yuv2.u) / 2; + dst[x * 2 + 2] = yuv2.y; + dst[x * 2 + 3] = (yuv1.v + yuv2.v) / 2; + break; + + default: + throw std::invalid_argument("fo"); + } + } + + dst += dst_fb.stride(0); + } +} + +static void fb_rgb_to_semiplanar_yuv(Framebuffer& dst_fb, const Framebuffer& src_fb) +{ + unsigned w = src_fb.width(); + unsigned h = src_fb.height(); + + uint8_t *dst_y = dst_fb.map(0); + uint8_t *dst_uv = dst_fb.map(1); + + for (unsigned y = 0; y < h; ++y) { + for (unsigned x = 0; x < w; ++x) { + YUV yuv = read_rgb_as_yuv(src_fb, x, y); + dst_y[x] = yuv.y; + } + + dst_y += dst_fb.stride(0); + } + + for (unsigned y = 0; y < h; y += 2) { + for (unsigned x = 0; x < w; x += 2) { + YUV yuv00 = read_rgb_as_yuv(src_fb, x + 0, y + 0); + YUV yuv01 = read_rgb_as_yuv(src_fb, x + 1, y + 0); + YUV yuv10 = read_rgb_as_yuv(src_fb, x + 0, y + 1); + YUV yuv11 = read_rgb_as_yuv(src_fb, x + 1, y + 1); + + unsigned u = (yuv00.u + yuv01.u + yuv10.u + yuv11.u) / 4; + unsigned v = (yuv00.v + yuv01.v + yuv10.v + yuv11.v) / 4; + + dst_uv[x + 0] = u; + dst_uv[x + 1] = v; + } + + dst_uv += dst_fb.stride(1); + } +} + +static void fb_rgb_to_rgb565(Framebuffer& dst_fb, const Framebuffer& src_fb) +{ + unsigned w = src_fb.width(); + unsigned h = src_fb.height(); + + uint8_t *dst = dst_fb.map(0); + + for (unsigned y = 0; y < h; ++y) { + for (unsigned x = 0; x < w; ++x) { + RGB rgb = read_rgb(src_fb, x, y); + + unsigned r = rgb.r * 32 / 256; + unsigned g = rgb.g * 64 / 256; + unsigned b = rgb.b * 32 / 256; + + ((uint16_t *)dst)[x] = (r << 11) | (g << 5) | (b << 0); + } + + dst += dst_fb.stride(0); + } +} + +void color_convert(Framebuffer& dst, const Framebuffer &src) +{ + switch (dst.format()) { + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + fb_rgb_to_semiplanar_yuv(dst, src); + break; + + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + fb_rgb_to_packed_yuv(dst, src); + break; + + case DRM_FORMAT_RGB565: + fb_rgb_to_rgb565(dst, src); + break; + + default: + throw std::invalid_argument("fo"); + } +} +} diff --git a/libkms++/utils/conv.h b/libkms++/utils/conv.h new file mode 100644 index 0000000..d1b306a --- /dev/null +++ b/libkms++/utils/conv.h @@ -0,0 +1,8 @@ +#pragma once + +namespace kms +{ +class Framebuffer; + +void color_convert(Framebuffer& dst, const Framebuffer &src); +} diff --git a/libkms++/utils/testpat.cpp b/libkms++/utils/testpat.cpp new file mode 100644 index 0000000..cd35e0e --- /dev/null +++ b/libkms++/utils/testpat.cpp @@ -0,0 +1,162 @@ + +#include <chrono> +#include <cstring> +#include <cassert> + +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <drm_fourcc.h> +#include <drm.h> +#include <drm_mode.h> + +#include "card.h" +#include "framebuffer.h" +#include "testpat.h" +#include "color.h" + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +namespace kms +{ +static void draw_pixel(Framebuffer& buf, unsigned x, unsigned y, RGB color) +{ + static RGB c1; + + switch (buf.format()) { + case DRM_FORMAT_XRGB8888: + { + uint32_t *p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4); + *p = color.raw; + break; + } + case DRM_FORMAT_RGB565: + { + uint16_t *p = (uint16_t*)(buf.map(0) + buf.stride(0) * y + x * 2); + *p = color.rgb565(); + break; + } + case DRM_FORMAT_UYVY: + { + if ((x & 1) == 0) { + c1 = color; + return; + } + + uint8_t *p = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x * 2); + + YUV yuv1 = c1.yuv(); + YUV yuv2 = color.yuv(); + + p[0] = (yuv1.u + yuv2.u) / 2; + p[1] = yuv1.y; + p[2] = (yuv1.v + yuv2.v) / 2; + p[3] = yuv2.y; + break; + } + case DRM_FORMAT_YUYV: + { + if ((x & 1) == 0) { + c1 = color; + return; + } + + uint8_t *p = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x * 2); + + YUV yuv1 = c1.yuv(); + YUV yuv2 = color.yuv(); + + p[0] = yuv1.y; + p[1] = (yuv1.u + yuv2.u) / 2; + p[2] = yuv2.y; + p[3] = (yuv1.v + yuv2.v) / 2; + break; + } + } +} + +static void draw_rgb_test_pattern(Framebuffer& fb) +{ + unsigned x, y; + unsigned w = fb.width(); + unsigned h = fb.height(); + + const unsigned mw = 20; + + const unsigned xm1 = mw; + const unsigned xm2 = w - mw - 1; + const unsigned ym1 = mw; + const unsigned ym2 = h - mw - 1; + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + // white margin lines + if (x == xm1 || x == xm2 || y == ym1 || y == ym2) + draw_pixel(fb, x, y, RGB(255, 255, 255)); + // white box outlines to corners + else if ((x == 0 || x == w - 1) && (y < ym1 || y > ym2)) + draw_pixel(fb, x, y, RGB(255, 255, 255)); + // white box outlines to corners + else if ((y == 0 || y == h - 1) && (x < xm1 || x > xm2)) + draw_pixel(fb, x, y, RGB(255, 255, 255)); + // blue bar on the left + else if (x < xm1 && (y > ym1 && y < ym2)) + draw_pixel(fb, x, y, RGB(0, 0, 255)); + // blue bar on the top + else if (y < ym1 && (x > xm1 && x < xm2)) + draw_pixel(fb, x, y, RGB(0, 0, 255)); + // red bar on the right + else if (x > xm2 && (y > ym1 && y < ym2)) + draw_pixel(fb, x, y, RGB(255, 0, 0)); + // red bar on the bottom + else if (y > ym2 && (x > xm1 && x < xm2)) + draw_pixel(fb, x, y, RGB(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) + draw_pixel(fb, x, y, RGB(255, 255, 255)); + // diagonal line + else if (w - x == y || x == h - y) + draw_pixel(fb, x, y, RGB(255, 255, 255)); + else { + int t = (x - xm1 - 1) * 3 / (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; + } + + draw_pixel(fb, x, y, RGB(r, g, b)); + } + // black corners + } else { + draw_pixel(fb, x, y, RGB(0, 0, 0)); + } + } + } +} + +void draw_test_pattern(Framebuffer& fb) +{ + using namespace std::chrono; + + auto t1 = high_resolution_clock::now(); + + draw_rgb_test_pattern(fb); + + auto t2 = high_resolution_clock::now(); + auto time_span = duration_cast<microseconds>(t2 - t1); + + printf("draw took %lu us\n", time_span.count()); +} +} diff --git a/libkms++/utils/testpat.h b/libkms++/utils/testpat.h new file mode 100644 index 0000000..b60271a --- /dev/null +++ b/libkms++/utils/testpat.h @@ -0,0 +1,6 @@ +#pragma once + +namespace kms +{ +void draw_test_pattern(Framebuffer& fb); +} |