modetest: util: pattern: add new patterns

Add three new test patterns:

- noise: random black and white pixels
- noise-color: random color pixels
- black-white: alternate between black and white pixels

These patterns are useful for measuring radiated emissions for EMC
compliance tests.

Signed-off-by: Emil Svendsen <emas@bang-olufsen.dk>
This commit is contained in:
Emil Svendsen 2024-05-17 15:04:35 +02:00
parent 76a1e97a9a
commit 40aeab6fd5
2 changed files with 291 additions and 0 deletions

View file

@ -23,6 +23,7 @@
* IN THE SOFTWARE. * IN THE SOFTWARE.
*/ */
#include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
@ -40,6 +41,13 @@
#include "format.h" #include "format.h"
#include "pattern.h" #include "pattern.h"
struct color_rgba {
uint16_t red;
uint16_t green;
uint16_t blue;
uint16_t alpha;
};
struct color_rgb24 { struct color_rgb24 {
unsigned int value:24; unsigned int value:24;
} __attribute__((__packed__)); } __attribute__((__packed__));
@ -301,6 +309,16 @@ static void write_pixels_10bpp(unsigned char *mem,
mem[4] = ((d >> 2) & 0xff); mem[4] = ((d >> 2) & 0xff);
} }
static void update_pixels_10bpp(unsigned char *mem, uint64_t val, uint64_t mask)
{
int i;
for (i = 0; i < 5; i++, mask >>= 8, val >>= 8) {
mem[i] &= ~(mask & 0xff);
mem[i] |= (mask & 0xff) & val;
}
}
static void fill_smpte_yuv_planar_10bpp(const struct util_yuv_info *yuv, static void fill_smpte_yuv_planar_10bpp(const struct util_yuv_info *yuv,
unsigned char *y_mem, unsigned char *y_mem,
unsigned char *uv_mem, unsigned char *uv_mem,
@ -1849,6 +1867,267 @@ static void fill_gradient(const struct util_format_info *info, void *planes[3],
} }
} }
static struct color_rgba get_black_white_value(uint64_t index)
{
const struct color_rgba colors[] = {
{ .red = 0, .green = 0, .blue = 0, .alpha = 255 }, /* black */
{ .red = 255, .green = 255, .blue = 255, .alpha = 255 }, /* white */
};
return colors[index & 0x1];
}
static struct color_rgba get_noise_color_value()
{
struct color_rgba color = {
.red = rand(),
.green = rand(),
.blue = rand(),
.alpha = 255
};
return color;
}
static struct color_rgba get_rgb_color(uint64_t index,
enum util_fill_pattern pattern)
{
struct color_rgba color = {
.red = 0,
.green = 0,
.blue = 0,
.alpha = 0
};
switch (pattern) {
case UTIL_PATTERN_NOISE:
color = get_black_white_value(rand());
case UTIL_PATTERN_NOISE_COLOR:
color = get_noise_color_value();
case UTIL_PATTERN_BLACK_WHITE:
color = get_black_white_value(index);
default:
break;
}
return color;
}
static void insert_value_yuv_packed(const struct util_format_info *info,
void *planes[3], unsigned int stride,
unsigned int x, unsigned int y,
const struct color_rgba* color)
{
struct color_yuv val = MAKE_YUV_601(color->red, color->green, color->blue);
const struct util_yuv_info *yuv = &info->yuv;
unsigned char *y_mem = (yuv->order & YUV_YC) ? planes[0] : planes[0] + 1;
unsigned char *c_mem = (yuv->order & YUV_CY) ? planes[0] : planes[0] + 1;
unsigned int u = (yuv->order & YUV_YCrCb) ? 2 : 0;
unsigned int v = (yuv->order & YUV_YCbCr) ? 2 : 0;
if (x & 0x1)
return;
y_mem += stride * y;
c_mem += stride * y;
y_mem[2*x] = val.y;
c_mem[2*x+u] = val.u;
y_mem[2*x+2] = val.y;
c_mem[2*x+v] = val.v;
}
static void insert_value_yuv_planar(const struct util_format_info *info,
void *planes[3], unsigned int stride,
unsigned int x, unsigned int y,
const struct color_rgba* color)
{
struct color_yuv val = MAKE_YUV_601(color->red, color->green, color->blue);
const struct util_yuv_info *yuv = &info->yuv;
unsigned int cs = yuv->chroma_stride;
unsigned int xsub = yuv->xsub;
unsigned int ysub = yuv->ysub;
unsigned int chroma_offset = (y + 1) / ysub;
unsigned char *y_mem = planes[0] + (y * stride);
unsigned char *u_mem = planes[1];
unsigned char *v_mem = planes[2];
switch (info->format) {
case DRM_FORMAT_NV42:
u_mem = info->yuv.order & YUV_YCbCr ? planes[1] : planes[1] + 1;
v_mem = info->yuv.order & YUV_YCrCb ? planes[1] : planes[1] + 1;
break;
case DRM_FORMAT_YVU420:
u_mem = planes[2];
v_mem = planes[1];
break;
case DRM_FORMAT_YUV420:
default:
break;
}
u_mem += (chroma_offset * (stride * cs / xsub));
v_mem += (chroma_offset * (stride * cs / xsub));
y_mem[x] = val.y;
u_mem[x/xsub*cs] = val.u;
v_mem[x/xsub*cs] = val.v;
}
inline bool is_power_of_two(unsigned long val)
{
return (val != 0) && ((val & (val - 1)) == 0);
}
static bool check_yuv(const struct util_yuv_info *info)
{
if (__builtin_expect(
is_power_of_two(info->xsub) &&
is_power_of_two(info->ysub) &&
is_power_of_two(info->chroma_stride), 1)) {
return true;
}
return false;
}
static void insert_value_yuv_planar_10bpp(const struct util_format_info *info,
void *planes[3], unsigned int stride,
unsigned int x, unsigned int y,
const struct color_rgba* color)
{
struct color_yuv val = MAKE_YUV_601(color->red, color->green, color->blue);
const struct util_yuv_info *yuv = &info->yuv;
unsigned int cs = yuv->chroma_stride;
unsigned int xsub = yuv->xsub;
unsigned int ysub = yuv->ysub;
unsigned int xstep = cs * xsub;
unsigned int ysub_mask = ysub - 1;
unsigned int xsub_mask = xsub - 1;
unsigned int xstep_mask = xstep - 1;
unsigned char *y_mem = planes[0] + (y * stride);
unsigned char *uv_mem = planes[1] + (y * (stride * cs / xsub));
unsigned int block_start = ((x & 0x3) * 5) / 4;
unsigned int bit_start = (x & 0x3) * 10;
/* Plus two because val.y is only 8 bits */
update_pixels_10bpp(&y_mem[block_start], val.y << (bit_start + 2),
0x3ff << bit_start);
/* This logic only works when xsub, ysub and chroma stride is power of two. */
assert(check_yuv(&info->yuv));
if (y & ysub_mask)
return;
if (x & xsub_mask)
return;
block_start = ((x & ~xstep_mask) * 5) / xstep;
bit_start = x & xstep_mask ? 0 : 20;
update_pixels_10bpp(&uv_mem[block_start],
((val.u << 2) | (val.v << 12)) << bit_start,
0xfffff << bit_start);
}
static void insert_value_rgb32(const struct util_format_info *info,
void *planes[3], unsigned int stride,
unsigned int x, unsigned int y,
const struct color_rgba* color)
{
uint32_t *row = planes[0] + (stride * y);
uint32_t val = MAKE_RGBA10(&info->rgb, color->red, color->green,
color->blue, color->alpha);
row[x] = val;
}
static void insert_value_rgb16fp(const struct util_format_info *info,
void *planes[3], unsigned int stride,
unsigned int x, unsigned int y,
const struct color_rgba* color)
{
uint64_t *row = planes[0] + (stride * y);
uint64_t val = MAKE_RGBA10FP16(&info->rgb, color->red, color->green,
color->blue, color->alpha);
row[x] = val;
}
static void fill_simple_patterns(const struct util_format_info *info,
void *planes[3], unsigned int width,
unsigned int height, unsigned int stride,
enum util_fill_pattern pattern)
{
void (*func_insert_value)(const struct util_format_info *info,
void *planes[3], unsigned int stride,
unsigned int x, unsigned int y,
const struct color_rgba* color);
int x, y;
switch (info->format) {
case DRM_FORMAT_UYVY:
case DRM_FORMAT_VYUY:
case DRM_FORMAT_YUYV:
case DRM_FORMAT_YVYU:
func_insert_value = &insert_value_yuv_packed;
break;
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV21:
case DRM_FORMAT_NV16:
case DRM_FORMAT_NV61:
case DRM_FORMAT_NV24:
case DRM_FORMAT_NV42:
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YVU420:
func_insert_value = &insert_value_yuv_planar;
break;
case DRM_FORMAT_NV15:
case DRM_FORMAT_NV20:
case DRM_FORMAT_NV30:
func_insert_value = &insert_value_yuv_planar_10bpp;
break;
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_XBGR8888:
case DRM_FORMAT_RGBA8888:
case DRM_FORMAT_RGBX8888:
case DRM_FORMAT_BGRA8888:
case DRM_FORMAT_BGRX8888:
case DRM_FORMAT_ARGB2101010:
case DRM_FORMAT_XRGB2101010:
case DRM_FORMAT_ABGR2101010:
case DRM_FORMAT_XBGR2101010:
case DRM_FORMAT_RGBA1010102:
case DRM_FORMAT_RGBX1010102:
case DRM_FORMAT_BGRA1010102:
case DRM_FORMAT_BGRX1010102:
func_insert_value = &insert_value_rgb32;
break;
case DRM_FORMAT_XRGB16161616F:
case DRM_FORMAT_XBGR16161616F:
case DRM_FORMAT_ARGB16161616F:
case DRM_FORMAT_ABGR16161616F:
func_insert_value = &insert_value_rgb16fp;
break;
default:
return;
}
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
struct color_rgba color = get_rgb_color((width * y) + x, pattern);
(*func_insert_value)(info, planes, stride, x, y, &color);
}
}
}
/* /*
* util_fill_pattern - Fill a buffer with a test pattern * util_fill_pattern - Fill a buffer with a test pattern
* @format: Pixel format * @format: Pixel format
@ -1884,6 +2163,12 @@ void util_fill_pattern(uint32_t format, enum util_fill_pattern pattern,
case UTIL_PATTERN_GRADIENT: case UTIL_PATTERN_GRADIENT:
return fill_gradient(info, planes, width, height, stride); return fill_gradient(info, planes, width, height, stride);
case UTIL_PATTERN_NOISE:
case UTIL_PATTERN_NOISE_COLOR:
case UTIL_PATTERN_BLACK_WHITE:
return fill_simple_patterns(info, planes, width, height, stride,
pattern);
default: default:
printf("Error: unsupported test pattern %u.\n", pattern); printf("Error: unsupported test pattern %u.\n", pattern);
break; break;
@ -1895,6 +2180,9 @@ static const char *pattern_names[] = {
[UTIL_PATTERN_SMPTE] = "smpte", [UTIL_PATTERN_SMPTE] = "smpte",
[UTIL_PATTERN_PLAIN] = "plain", [UTIL_PATTERN_PLAIN] = "plain",
[UTIL_PATTERN_GRADIENT] = "gradient", [UTIL_PATTERN_GRADIENT] = "gradient",
[UTIL_PATTERN_NOISE] = "noise",
[UTIL_PATTERN_NOISE_COLOR] = "noise-color",
[UTIL_PATTERN_BLACK_WHITE] = "black-white",
}; };
enum util_fill_pattern util_pattern_enum(const char *name) enum util_fill_pattern util_pattern_enum(const char *name)

View file

@ -33,6 +33,9 @@ enum util_fill_pattern {
UTIL_PATTERN_PLAIN, UTIL_PATTERN_PLAIN,
UTIL_PATTERN_SMPTE, UTIL_PATTERN_SMPTE,
UTIL_PATTERN_GRADIENT, UTIL_PATTERN_GRADIENT,
UTIL_PATTERN_NOISE,
UTIL_PATTERN_NOISE_COLOR,
UTIL_PATTERN_BLACK_WHITE,
}; };
void util_fill_pattern(uint32_t format, enum util_fill_pattern pattern, void util_fill_pattern(uint32_t format, enum util_fill_pattern pattern,