75#define BLANK_USAGE "blank[=0x<AABBGGRR>]"
76#define MOD_NAME "[vid. patt. generator] "
81using namespace std::string_literals;
84using std::default_random_engine;
87using std::make_unique;
92using std::string_view;
95using std::uniform_int_distribution;
108 vector<unsigned char> out(data_len);
109 auto actual_bit_depth =
fill(width, height, out.data());
130 for (
int y = height - 1; y >= 0; --y) {
131 for (
int x = width - 1; x >= 0; --x) {
132 unsigned char *in_pix = data + 4 * (y * width + x);
133 unsigned char *out_pix = data + 6 * (y * width + x);
134 unsigned char r = *in_pix++;
135 unsigned char g = *in_pix++;
136 unsigned char b = *in_pix++;
148 for (
int y = 0; y < height; ++y) {
149 for (
int x = 0; x < width; ++x) {
150 unsigned char *in_pix = data + 6 * (y * width + x);
151 unsigned char *out_pix = data + 4 * (y * width + x);
152 *out_pix++ = in_pix[1];
153 *out_pix++ = in_pix[3];
154 *out_pix++ = in_pix[5];
164 if (
init ==
"help"s) {
165 col() <<
"Testcard bar usage:\n\t" <<
SBOLD(
SRED(
"-t testcard:pattern=bars") <<
"[=text]") <<
" - optionally annotate with text" <<
"\n";
179 rect_size = (width + rect_size - 1) / rect_size;
180 for (
int j = 0; j < height; j += rect_size) {
181 uint32_t grey = 0xFF010101U;
182 if (j == rect_size * 2) {
188 r.
h = rect_size - (rect_size * 3 / 4);
189 r.
y = j + rect_size * 3 / 4;
192 for (
int i = 0; i < width; i += rect_size) {
198 if (j != rect_size * 2) {
201 col_num = (col_num + 1) %
COL_NUM;
204 r.
y += rect_size / 4;
206 grey += 0x00010101U * (255 /
COL_NUM);
221template<u
int8_t f,
int columns>
223 static constexpr uint32_t
bars[] = {
224 uint32_t{0xFFU << 24U | f << 16U | f << 8U | f },
225 uint32_t{0xFFU << 24U | 0U << 16U | f << 8U | f },
226 uint32_t{0xFFU << 24U | f << 16U | f << 8U | 0U },
227 uint32_t{0xFFU << 24U | 0U << 16U | f << 8U | 0U },
228 uint32_t{0xFFU << 24U | f << 16U | 0U << 8U | f },
229 uint32_t{0xFFU << 24U | 0U << 16U | 0U << 8U | f },
230 uint32_t{0xFFU << 24U | f << 16U | 0U << 8U | 0U },
231 uint32_t{0xFFU << 24U | 0U << 16U | 0U << 8U | 0U },
235 const int rect_size = (width + columns - 1) / columns;
241 for (
int j = 0; j < height; j += rect_size) {
242 for (
int i = 0; i < width; i += rect_size) {
250 col_num = (col_num + 1) % columns;
260 uint32_t{0xFFU << 24U | 105 << 16U | 63 << 8U | 0U },
261 uint32_t{0xFFFFFFFFU },
262 uint32_t{0xFFU << 24U | 119U << 16U | 0U << 8U | 0U },
263 uint32_t{0xFF000000U },
264 uint32_t{0xFF000000U },
265 uint32_t{0xFF000000U },
272 const int mid_strip_height = height / 3 - width / 6;
273 struct testcard_rect r{ .
x = 0, .y = height / 3 * 2, .w = (width + columns - 1) / columns, .h = mid_strip_height};
274 for (
int i = 0; i < columns; i += 1) {
283 0xBFU, 7>
::bars[columns - 1 - i]);
287 r.
w = (width + columns - 1) / columns;
289 r.
y += mid_strip_height;
290 for (
int i = 0; i < columns; i += 1) {
297 r.
x = 5 * (width / 7);
298 r.
w = (width / 7) / 3;
302 0xFFU << 24 | 0x0A0A0A);
310 if (
init ==
"help"s) {
315 "\nLeading zeros can be omitted, eg. "
316 "`0xFF` produces a red pattern.\n");
318 "Defaults to 0xFF000000.\n");
328 for (
int i = 0; i < width * height; ++i) {
329 (
reinterpret_cast<uint32_t *
>(data))[i] =
color;
339 if (!config.empty()) {
340 color = stol(config,
nullptr, 0);
343 static constexpr uint32_t
red = 0xFFU;
346 auto *ptr =
reinterpret_cast<uint16_t *
>(data);
347 for (
int j = 0; j < height; j += 1) {
348 uint16_t r = sin(
static_cast<double>(j) / height * M_PI) * (
color & 0xFFU) / 0xFFU * 0xFF'FFU;
349 uint16_t g = sin(
static_cast<double>(j) / height * M_PI) * ((
color >> 8) & 0xFFU) / 0xFFU * 0xFF'FFU;
350 uint16_t b = sin(
static_cast<double>(j) / height * M_PI) * ((
color >> 16) & 0xFFU) / 0xFFU * 0xFF'FFU;
351 for (
int i = 0; i < width; i += 1) {
365 if (config.empty()) {
368 if (config ==
"help"s) {
369 cout <<
"Testcard gradient2 usage:\n\t-t testcard:gradient2[=maxval] - maxval is 16-bit number\n";
372 val_max = stol(config,
nullptr, 0);
378 auto *ptr =
reinterpret_cast<uint16_t *
>(data);
379 for (
int j = 0; j < height; j += 1) {
380 for (
int i = 0; i < width; i += 1) {
381 unsigned int gray = i *
val_max / (width - 1);
394 if (!y_lvl.empty()) {
401 assert(width > 1 && height > 1);
402 auto *ptr =
reinterpret_cast<uint16_t *
>(data);
405 for (
int j = 0; j < height; j += 1) {
406 for (
int i = 0; i < width; i += 1) {
408 uyva[0] =
LIMIT_LO(16) + i * scale_cbcr / (width - 1);
410 uyva[2] =
LIMIT_LO(16) + j * scale_cbcr / (height - 1);
423 uniform_int_distribution<> dist(0, 0xFFFF);
424 for_each(
reinterpret_cast<uint16_t *
>(data),
reinterpret_cast<uint16_t *
>(data) + 3 * width * height, [&](uint16_t & c) { c = dist(
rand_gen); });
433 for (
int i = 0; i <
COL_NUM; ++i) {
442 if (config.empty()) {
445 if (config ==
"help"s) {
448 "testcard:patt=strips[=[vert|hor|"
449 "diag][,w[idth]=W]]")
"\n");
455 char *endptr =
nullptr;
456 while (
char *
item = strtok_r(tmp,
",", &endptr)) {
458 if (strncmp(
item,
"hor", 3) == 0) {
460 }
else if (strncmp(
item,
"ver", 3) == 0) {
462 }
else if (strncmp(
item,
"dia", 3) == 0) {
468 string(
"Wrong option: ") + config);
477 unsigned char *data)
override
479 auto *ptr =
reinterpret_cast<uint32_t *
>(data);
480 for (
int j = 0; j < height; j += 1) {
481 for (
int i = 0; i < width; i += 1) {
482 const int col_idx =
type ==
DIAG ? i + j
496 if (config.empty()) {
499 if (config.substr(0,
"0x"s.length()) ==
"0x") {
500 config = config.substr(2);
502 while (!config.empty()) {
503 unsigned char byte = 0;
504 if (sscanf(config.c_str(),
"%2hhx", &
byte) == 1) {
507 config = config.substr(min<size_t>(config.size(), 2));
511 void raw_fill(
unsigned char *data,
size_t data_len) {
520 memset(data, 0, width * height * 3);
529 if (config ==
"help"s) {
530 col() <<
"Testcard text usage:\n\t" <<
SBOLD(
SRED(
"-t testcard:pattern=text") <<
"[=pattern][,bg=0x<AABBGGRR<][,fg=0x<AABBGGRR>]") <<
"\n";
533 if (!config.empty()) {
534 string_view sv = config;
535 bool text_set =
false;
536 while (!sv.empty()) {
538 if (tok.substr(0,3) ==
"bg=") {
539 bg = stol((
string) tok.substr(3),
nullptr, 0);
540 }
else if (tok.substr(0,3) ==
"fg=") {
541 fg = stol((
string) tok.substr(3),
nullptr, 0);
542 }
else if (!text_set) {
554 std::fill((uint32_t *)(
void *) data, (uint32_t *)(
void *) (data + width * height * 4),
bg);
556 for (
int i = 0; i < width; i += 8 * (
text.size() + 1)) {
559 for (
int i = 0; i < height / 16; ++i) {
560 draw_line((
char *) data + (i * 16 * width * 4), width * 4, line.c_str(),
fg,
false);
564 string text =
" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
565 uint32_t
bg = 0xFFCC00CCU;
566 uint32_t
fg = 0xFFFFFFFFU;
572 if (config ==
"help"s) {
573 col() <<
"Testcard diagonal usage:\n\t" <<
SBOLD(
SRED(
"-t testcard:pattern=diagonal") <<
"[,bg=0x<AABBGGRR<][,fg=0x<AABBGGRR>][,stride=<stride>][,line_width=<width>]") <<
"\n";
576 if (!config.empty()) {
577 string_view sv = config;
578 while (!sv.empty()) {
583 bg = stol((
string)
val,
nullptr, 0);
584 }
else if (
key ==
"fg") {
585 fg = stol((
string)
val,
nullptr, 0);
586 }
else if (
key ==
"stride") {
588 }
else if (
key ==
"line_width") {
599 std::fill((uint32_t *)(
void *) data, (uint32_t *)(
void *) (data + width * height * 4),
bg);
600 for(
int y = 0; y < height; y++){
603 *((uint32_t *) (
void *) (data + (y*width + x + i) * 4)) =
fg;
608 *((uint32_t *) (
void *) (data + (y*width + x + i) * 4)) =
fg;
615 uint32_t
bg = 0xFF000000U;
616 uint32_t
fg = 0xFFFFFFFFU;
623 return make_unique<image_pattern_bars>(
params);
626 return make_unique<image_pattern_blank>(
params);
629 return make_unique<image_pattern_ebu_smpte_bars<0xFFU, 8>>();
632 return make_unique<image_pattern_gradient>(
params);
635 return make_unique<image_pattern_gradient2>(
params);
638 return make_unique<image_pattern_noise>();
641 return make_unique<image_pattern_raw>(
params);
644 return make_unique<image_pattern_smpte_bars>();
647 return make_unique<image_pattern_strips>(
params);
650 return make_unique<image_pattern_text>(
params);
653 return make_unique<image_pattern_uv_plane>(
params);
656 return make_unique<image_pattern_diagonal>(
params);
670 unique_ptr<image_pattern> generator;
673 }
catch (exception
const &e) {
688 vector<unsigned char> src;
724 if (opts ==
"help") {
725 col() <<
"Usage:\n\t" SBOLD(
"gray[:step]") <<
" - interframe color increment (default " << DEFAULT_STEP <<
")\n";
734 for (
int i = 0; i <
pixels * 4; ++i) {
735 rgba[i] = (i + 1) % 4 != 0 ?
col : 0xFFU;
741 auto next_frame = vector<unsigned char>(
data_len);
742 for (
int y = 0; y <
height; ++y) {
747 data.push_back(std::move(next_frame));
752 auto *out = (
char *) data[cur_idx++].data();
753 if (cur_idx * step >= 0xFF) {
760 constexpr static int DEFAULT_STEP = 1;
761 int step = DEFAULT_STEP;
767 vector<vector<unsigned char>>
data;
776 string(
"[interlaced] width must be divisible by ") +
777 std::to_string(
step) +
"!");
780 vector<char> rgb(3 * h * rgb_linesize + 4 * (
width /
step) * rgb_linesize);
781 memset(rgb.data(), 255, h * rgb_linesize);
782 char *ptr = rgb.data() + h * rgb_linesize;
783 auto fill = [&](
int col1,
int col2) {
785 size_t fill_len = rgb_linesize - i * 3;
786 memset(ptr, col1, fill_len);
787 memset(ptr + fill_len, col2, rgb_linesize - fill_len);
789 fill_len =
MAX(0, (
int) rgb_linesize - (i + 2 *
step) * 3);
790 memset(ptr, col1, fill_len);
791 memset(ptr + fill_len, col2, rgb_linesize - fill_len);
796 memset(ptr, 0, h * rgb_linesize);
797 ptr += h * rgb_linesize;
799 memset(ptr, 255, h * rgb_linesize);
800 vector<char> rgba(rgb.size() / 3 * 4);
801 for (
unsigned i = 0; i < rgb.size(); i += 3) {
802 rgba[i / 3 * 4] = rgb[i];
803 rgba[i / 3 * 4 + 1] = rgb[i + 1];
804 rgba[i / 3 * 4 + 2] = rgb[i + 2];
805 rgba[i / 3 * 4 + 3] = 0xff;
833 if (
string(config) ==
"help") {
834 col() <<
"Pattern to use, one of: \n";
841 "gradient[=0x<AABBGGRR>]",
849 "uv_plane[=<y_lvl>]",
851 col() <<
"\t- " <<
SBOLD(p) <<
"\n";
854 col() <<
"\nNotes:\n";
856 col() <<
"\t- patterns " SBOLD(
"'gradient'")
", "
857 SBOLD(
"'gradient2'")
", " SBOLD(
"'noise'")
" and "
858 SBOLD(
"'uv_plane'")
" generate higher bit-depth "
861 c =
static_cast<codec_t>(
static_cast<int>(c) + 1)) {
867 col() <<
"\t- pattern "
868 <<
SBOLD(
"'raw'")
" generates repeating sequence of given "
869 "bytes without any color conversion\n";
870 col() <<
"\t- patterns marked with "
871 <<
SBOLD(
"'*'")
" provide help as its option\n";
874 assert(width > 0 && height > 0);
878 if (string::size_type delim =
pattern.find(
'='); delim != string::npos) {
889 }
catch (exception
const &e) {
905 memcpy(
state.data.data(), data,
state.data_len);
int vc_get_linesize(unsigned int width, codec_t codec)
const char * get_codec_name(codec_t codec)
enum generator_depth fill(int width, int height, unsigned char *data) override
image_pattern_bars(string const &init)
image_pattern_blank(string const &init)
enum generator_depth fill(int width, int height, unsigned char *data) override
enum generator_depth fill(int width, int height, unsigned char *data) override
image_pattern_diagonal(const string &config)
static constexpr uint32_t bars[]
enum generator_depth fill(int width, int height, unsigned char *data) override
image_pattern_gradient2(string const &config)
enum generator_depth fill(int width, int height, unsigned char *data) override
static constexpr uint32_t red
image_pattern_gradient(const string &config)
enum generator_depth fill(int width, int height, unsigned char *data) override
default_random_engine rand_gen
enum generator_depth fill(int width, int height, unsigned char *data) override
vector< unsigned char > m_pattern
enum generator_depth fill(int width, int height, unsigned char *data) override
void raw_fill(unsigned char *data, size_t data_len)
image_pattern_raw(string config)
enum generator_depth fill(int width, int height, unsigned char *data) override
static constexpr uint32_t bottom_bars[]
image_pattern_text(const string &config)
enum generator_depth fill(int width, int height, unsigned char *data) override
enum generator_depth fill(int width, int height, unsigned char *data) override
image_pattern_uv_plane(string const &y_lvl)
image_pattern(const image_pattern &)=delete
image_pattern(image_pattern &&)=delete
virtual void convert_rg48_to_rgba(int width, int height, unsigned char *data)
auto init(int width, int height, enum generator_depth depth) noexcept
virtual ~image_pattern()=default
virtual enum generator_depth fill(int width, int height, unsigned char *data)=0
image_pattern & operator=(const image_pattern &)=delete
virtual void convert_rgba_to_rg48(int width, int height, unsigned char *data)
image_pattern && operator=(image_pattern &&)=delete
static unique_ptr< image_pattern > create(string const &pattern, string const ¶ms)
#define LIMIT_HI_CBCR(depth)
#define LIMIT_HI_Y(depth)
int color_printf(const char *format,...)
static __device__ const float offset
void log_msg(int level, const char *format,...)
#define LOG_LEVEL_ERROR
general errors
static struct @19 params[100]
#define snprintf_ch(str,...)
shortcut for snprintf(var, sizeof var...), var must be a char array
#define IS_KEY_PREFIX(tok, key)
expands to true value if <k> from tok in format <k>=<v> is prefix of key
@ STR_LEN
"standard" string length placeholder
decoder_t get_decoder_from_to(codec_t in, codec_t out)
std::string_view tokenize(std::string_view &str, char delim, char quot)
Tokenizer for string_view.
gray_video_pattern_generator(int w, int h, codec_t c, string const &opts)
char * get_next() override
vector< vector< unsigned char > > data
enum generator_depth fill(int width, int height, unsigned char *data) override
void parse_fmt(string const &config)
image_pattern_strips(string const &config)
enum image_pattern_strips::@65 type
char * get_next() override
static constexpr int step
interlaced_video_pattern_generator(int w, int h, codec_t color_spec)
still_image_video_pattern_generator(string const &pattern, string const ¶ms, int w, int h, codec_t c, int o)
vector< unsigned char > data
char * get_next() override
virtual ~video_pattern_generator()
virtual char * get_next()=0
const uint32_t rect_colors[]
void testcard_fillRect(struct testcard_pixmap *s, struct testcard_rect *r, uint32_t color)
void testcard_convert_buffer(codec_t in_c, codec_t out_c, unsigned char *out, unsigned const char *in, int width, int height)
@ RGB
RGB 8-bit (packed into 24-bit word)
@ RG48
16-bit RGB little-endian (RlRhGl...)
@ Y416
interleaved little-endian YCbCr 4444 16-bit - UYVA
@ VIDEO_CODEC_COUNT
count of known video codecs (including VIDEO_CODEC_NONE)
@ RGBA
RGBA 8-bit, big-endian.
bool draw_line(char *buf, int pitch, const char *text, uint32_t color, bool solid)
This is an umbrella header for video functions.
int get_pf_block_bytes(codec_t codec)
returns codec_info_t::block_size_bytes
int get_pf_block_pixels(codec_t codec)
returns codec_info_t::block_size_pixels
size_t vc_get_datalen(unsigned int width, unsigned int height, codec_t codec)
int get_bits_per_component(codec_t codec)
#define MAX_PFB_SIZE
maximal pixfmt block size (R12L - 8 B)
void video_pattern_generator_fill_data(video_pattern_generator_t s, const char *data)
char * video_pattern_generator_next_frame(video_pattern_generator_t s)
constexpr size_t headroom
video_pattern_generator_t video_pattern_generator_create(const char *config, int width, int height, codec_t color_spec, int offset)
void video_pattern_generator_destroy(video_pattern_generator_t s)