diff options
Diffstat (limited to '')
-rw-r--r-- | spa/plugins/videotestsrc/draw.c | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/spa/plugins/videotestsrc/draw.c b/spa/plugins/videotestsrc/draw.c new file mode 100644 index 0000000..20fed49 --- /dev/null +++ b/spa/plugins/videotestsrc/draw.c @@ -0,0 +1,288 @@ +/* Spa Video Test Source + * + * Copyright © 2018 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <errno.h> + +typedef enum { + GRAY = 0, + YELLOW, + CYAN, + GREEN, + MAGENTA, + RED, + BLUE, + BLACK, + NEG_I, + WHITE, + POS_Q, + DARK_BLACK, + LIGHT_BLACK, + N_COLORS +} Color; + +typedef struct _Pixel Pixel; + +struct _Pixel { + unsigned char R; + unsigned char G; + unsigned char B; + unsigned char Y; + unsigned char U; + unsigned char V; +}; + +static Pixel colors[N_COLORS] = { + {191, 191, 191, 0, 0, 0}, /* GRAY */ + {191, 191, 0, 0, 0, 0}, /* YELLOW */ + {0, 191, 191, 0, 0, 0}, /* CYAN */ + {0, 191, 0, 0, 0, 0}, /* GREEN */ + {191, 0, 191, 0, 0, 0}, /* MAGENTA */ + {191, 0, 0, 0, 0, 0}, /* RED */ + {0, 0, 191, 0, 0, 0}, /* BLUE */ + {19, 19, 19, 0, 0, 0}, /* BLACK */ + {0, 33, 76, 0, 0, 0}, /* NEGATIVE I */ + {255, 255, 255, 0, 0, 0}, /* WHITE */ + {49, 0, 107, 0, 0, 0}, /* POSITIVE Q */ + {9, 9, 9, 0, 0, 0}, /* DARK BLACK */ + {29, 29, 29, 0, 0, 0}, /* LIGHT BLACK */ +}; + +/* YUV values are computed in init_colors() */ + +typedef struct _DrawingData DrawingData; + +typedef void (*DrawPixelFunc) (DrawingData * dd, int x, Pixel * pixel); + +struct _DrawingData { + char *line; + int width; + int height; + int stride; + DrawPixelFunc draw_pixel; +}; + +static inline void update_yuv(Pixel * pixel) +{ + uint16_t y, u, v; + + /* see https://en.wikipedia.org/wiki/YUV#Studio_swing_for_BT.601 */ + + y = 76 * pixel->R + 150 * pixel->G + 29 * pixel->B; + u = -43 * pixel->R - 84 * pixel->G + 127 * pixel->B; + v = 127 * pixel->R - 106 * pixel->G - 21 * pixel->B; + + y = (y + 128) >> 8; + u = (u + 128) >> 8; + v = (v + 128) >> 8; + + pixel->Y = y; + pixel->U = u + 128; + pixel->V = v + 128; +} + +static void init_colors(void) +{ + int i; + + if (colors[WHITE].Y != 0) { + /* already computed */ + return; + } + + for (i = 0; i < N_COLORS; i++) { + update_yuv(&colors[i]); + } +} + +static void draw_pixel_rgb(DrawingData * dd, int x, Pixel * color) +{ + dd->line[3 * x + 0] = color->R; + dd->line[3 * x + 1] = color->G; + dd->line[3 * x + 2] = color->B; +} + +static void draw_pixel_uyvy(DrawingData * dd, int x, Pixel * color) +{ + if (x & 1) { + /* odd pixel */ + dd->line[2 * (x - 1) + 3] = color->Y; + } else { + /* even pixel */ + dd->line[2 * x + 0] = color->U; + dd->line[2 * x + 1] = color->Y; + dd->line[2 * x + 2] = color->V; + } +} + +static int drawing_data_init(DrawingData * dd, struct impl *this, char *data) +{ + struct port *port = &this->port; + struct spa_video_info *format = &port->current_format; + struct spa_rectangle *size = &format->info.raw.size; + + if ((format->media_type != SPA_MEDIA_TYPE_video) || + (format->media_subtype != SPA_MEDIA_SUBTYPE_raw)) + return -ENOTSUP; + + if (format->info.raw.format == SPA_VIDEO_FORMAT_RGB) { + dd->draw_pixel = draw_pixel_rgb; + } else if (format->info.raw.format == SPA_VIDEO_FORMAT_UYVY) { + dd->draw_pixel = draw_pixel_uyvy; + } else + return -ENOTSUP; + + dd->line = data; + dd->width = size->width; + dd->height = size->height; + dd->stride = port->stride; + + return 0; +} + +static inline void draw_pixels(DrawingData * dd, int offset, Color color, int length) +{ + int x; + + for (x = offset; x < offset + length; x++) { + dd->draw_pixel(dd, x, &colors[color]); + } +} + +static inline void next_line(DrawingData * dd) +{ + dd->line += dd->stride; +} + +static void draw_smpte_snow(DrawingData * dd) +{ + int h, w; + int y1, y2; + int i, j; + + w = dd->width; + h = dd->height; + y1 = 2 * h / 3; + y2 = 3 * h / 4; + + for (i = 0; i < y1; i++) { + for (j = 0; j < 7; j++) { + int x1 = j * w / 7; + int x2 = (j + 1) * w / 7; + draw_pixels(dd, x1, j, x2 - x1); + } + next_line(dd); + } + + for (i = y1; i < y2; i++) { + for (j = 0; j < 7; j++) { + int x1 = j * w / 7; + int x2 = (j + 1) * w / 7; + Color c = (j & 1) ? BLACK : BLUE - j; + + draw_pixels(dd, x1, c, x2 - x1); + } + next_line(dd); + } + + for (i = y2; i < h; i++) { + int x = 0; + + /* negative I */ + draw_pixels(dd, x, NEG_I, w / 6); + x += w / 6; + + /* white */ + draw_pixels(dd, x, WHITE, w / 6); + x += w / 6; + + /* positive Q */ + draw_pixels(dd, x, POS_Q, w / 6); + x += w / 6; + + /* pluge */ + draw_pixels(dd, x, DARK_BLACK, w / 12); + x += w / 12; + draw_pixels(dd, x, BLACK, w / 12); + x += w / 12; + draw_pixels(dd, x, LIGHT_BLACK, w / 12); + x += w / 12; + + /* war of the ants (a.k.a. snow) */ + for (j = x; j < w; j++) { + Pixel p; + unsigned char r = rand(); + + p.R = r; + p.G = r; + p.B = r; + update_yuv(&p); + dd->draw_pixel(dd, j, &p); + } + + next_line(dd); + } +} + +static void draw_snow(DrawingData * dd) +{ + int x, y; + + for (y = 0; y < dd->height; y++) { + for (x = 0; x < dd->width; x++) { + Pixel p; + unsigned char r = rand(); + + p.R = r; + p.G = r; + p.B = r; + update_yuv(&p); + dd->draw_pixel(dd, x, &p); + } + + next_line(dd); + } +} + +static int draw(struct impl *this, char *data) +{ + DrawingData dd; + int res; + + init_colors(); + + if ((res = drawing_data_init(&dd, this, data)) < 0) + return res; + + switch (this->props.pattern) { + case PATTERN_SMPTE_SNOW: + draw_smpte_snow(&dd); + break; + case PATTERN_SNOW: + draw_snow(&dd); + break; + default: + return -ENOTSUP; + } + return 0; +} |