From 636047a4e09c6e3a9bacae1e7caae788212651c6 Mon Sep 17 00:00:00 2001 From: Filipe Cavalcanti Date: Mon, 27 Jul 2026 13:57:40 -0300 Subject: [PATCH] examples/fb: add SMPTE color bar example Add a -p option to select the pattern to draw. The existing expanding rectangles animation remains the default, and a new "smpte" pattern fills the display with seven equal vertical bars (white, yellow, cyan, green, magenta, red and blue), which is useful to verify color ordering and channel wiring of a framebuffer display. To support more than one palette, the draw_rect() helpers now receive the color value to write instead of an index into the per-format color tables. Monochrome displays pick black or white per bar using a BT.601 luma threshold. The FBIO_UPDATE ioctl is moved out of draw_rect() into a new present_area() helper, so the SMPTE pattern can draw every bar and then update the display only once. Assisted-by: Cursor:claude-opus-5 Signed-off-by: Filipe Cavalcanti --- examples/fb/fb_main.c | 223 +++++++++++++++++++++++++++++------------- 1 file changed, 157 insertions(+), 66 deletions(-) diff --git a/examples/fb/fb_main.c b/examples/fb/fb_main.c index bd7d457e1..3d237df05 100644 --- a/examples/fb/fb_main.c +++ b/examples/fb/fb_main.c @@ -43,7 +43,11 @@ * Preprocessor Definitions ****************************************************************************/ -#define NCOLORS 6 +#define NRECTCOLORS 6 +#define NSMPTECOLORS 7 + +#define PATTERN_RECTANGLES 0 +#define PATTERN_SMPTE 1 /**************************************************************************** * Private Types @@ -71,24 +75,51 @@ static const char g_default_fbdev[] = CONFIG_EXAMPLES_FB_DEFAULTFB; /* Violet-Blue-Green-Yellow-Orange-Red */ -static const uint32_t g_rgb24[NCOLORS] = +static const uint32_t g_rgb24[NRECTCOLORS] = { RGB24_VIOLET, RGB24_BLUE, RGB24_GREEN, RGB24_YELLOW, RGB24_ORANGE, RGB24_RED }; -static const uint16_t g_rgb16[NCOLORS] = +static const uint16_t g_rgb16[NRECTCOLORS] = { RGB16_VIOLET, RGB16_BLUE, RGB16_GREEN, RGB16_YELLOW, RGB16_ORANGE, RGB16_RED }; -static const uint8_t g_rgb8[NCOLORS] = +static const uint8_t g_rgb8[NRECTCOLORS] = { RGB8_VIOLET, RGB8_BLUE, RGB8_GREEN, RGB8_YELLOW, RGB8_ORANGE, RGB8_RED }; +/* White-Yellow-Cyan-Green-Magenta-Red-Blue */ + +static const uint32_t g_smpte_rgb24[NSMPTECOLORS] = +{ + RGB24_WHITE, RGB24_YELLOW, RGB24_CYAN, RGB24_GREEN, + RGB24_MAGENTA, RGB24_RED, RGB24_BLUE +}; + +static const uint16_t g_smpte_rgb16[NSMPTECOLORS] = +{ + RGB16_WHITE, RGB16_YELLOW, RGB16_CYAN, RGB16_GREEN, + RGB16_MAGENTA, RGB16_RED, RGB16_BLUE +}; + +static const uint8_t g_smpte_rgb8[NSMPTECOLORS] = +{ + RGB8_WHITE, RGB8_YELLOW, RGB8_CYAN, RGB8_GREEN, + RGB8_MAGENTA, RGB8_RED, RGB8_BLUE +}; + +/* Monochrome equivalents selected using BT.601 luma. */ + +static const uint8_t g_smpte_mono[NSMPTECOLORS] = +{ + 1, 1, 1, 1, 0, 0, 0 +}; + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -270,7 +301,7 @@ static int fb_init_mem2(FAR struct fb_state_s *state) ****************************************************************************/ static void draw_rect32(FAR struct fb_state_s *state, - FAR struct fb_area_s *area, int color) + FAR struct fb_area_s *area, uint32_t color) { FAR uint32_t *dest; FAR uint8_t *row; @@ -283,7 +314,7 @@ static void draw_rect32(FAR struct fb_state_s *state, dest = ((FAR uint32_t *)row) + area->x; for (x = 0; x < area->w; x++) { - *dest++ = g_rgb24[color] | 0xff000000; + *dest++ = color | 0xff000000; } row += state->pinfo.stride; @@ -291,7 +322,7 @@ static void draw_rect32(FAR struct fb_state_s *state, } static void draw_rect24(FAR struct fb_state_s *state, - FAR struct fb_area_s *area, int color) + FAR struct fb_area_s *area, uint32_t color) { FAR uint8_t *dest; FAR uint8_t *row; @@ -304,9 +335,9 @@ static void draw_rect24(FAR struct fb_state_s *state, dest = ((FAR uint8_t *)row) + area->x * 3; for (x = 0; x < area->w; x++) { - *dest++ = g_rgb24[color] & 0xff; - *dest++ = (g_rgb24[color] >> 8) & 0xff; - *dest++ = (g_rgb24[color] >> 16) & 0xff; + *dest++ = color & 0xff; + *dest++ = (color >> 8) & 0xff; + *dest++ = (color >> 16) & 0xff; } row += state->pinfo.stride; @@ -314,7 +345,7 @@ static void draw_rect24(FAR struct fb_state_s *state, } static void draw_rect16(FAR struct fb_state_s *state, - FAR struct fb_area_s *area, int color) + FAR struct fb_area_s *area, uint16_t color) { FAR uint16_t *dest; FAR uint8_t *row; @@ -327,7 +358,7 @@ static void draw_rect16(FAR struct fb_state_s *state, dest = ((FAR uint16_t *)row) + area->x; for (x = 0; x < area->w; x++) { - *dest++ = g_rgb16[color]; + *dest++ = color; } row += state->pinfo.stride; @@ -335,7 +366,7 @@ static void draw_rect16(FAR struct fb_state_s *state, } static void draw_rect8(FAR struct fb_state_s *state, - FAR struct fb_area_s *area, int color) + FAR struct fb_area_s *area, uint8_t color) { FAR uint8_t *dest; FAR uint8_t *row; @@ -348,7 +379,7 @@ static void draw_rect8(FAR struct fb_state_s *state, dest = row + area->x; for (x = 0; x < area->w; x++) { - *dest++ = g_rgb8[color]; + *dest++ = color; } row += state->pinfo.stride; @@ -356,11 +387,11 @@ static void draw_rect8(FAR struct fb_state_s *state, } static void draw_rect1(FAR struct fb_state_s *state, - FAR struct fb_area_s *area, int color) + FAR struct fb_area_s *area, uint8_t color) { FAR uint8_t *pixel; FAR uint8_t *row; - uint8_t color8 = (color & 1) == 0 ? 0 : 0xff; + uint8_t color8 = color == 0 ? 0 : 0xff; int start_full_x; int end_full_x; @@ -437,43 +468,47 @@ static void draw_rect1(FAR struct fb_state_s *state, } static void draw_rect(FAR struct fb_state_s *state, - FAR struct fb_area_s *area, int color) + FAR struct fb_area_s *area, uint32_t color24, + uint16_t color16, uint8_t color8, uint8_t mono) { -#ifdef CONFIG_FB_UPDATE - int ret; -#endif - switch (state->pinfo.bpp) { case 32: - draw_rect32(state, area, color); + draw_rect32(state, area, color24); break; case 24: - draw_rect24(state, area, color); + draw_rect24(state, area, color24); break; case 16: - draw_rect16(state, area, color); + draw_rect16(state, area, color16); break; case 8: default: - draw_rect8(state, area, color); + draw_rect8(state, area, color8); break; case 1: - draw_rect1(state, area, color); + draw_rect1(state, area, mono); break; } +} +static void present_area(FAR struct fb_state_s *state, + FAR const struct fb_area_s *area) +{ #ifdef CONFIG_FB_UPDATE + struct fb_area_s update = *area; + int ret; + int yoffset = state->act_fbmem == state->fbmem ? 0 : state->mem2_yoffset; - area->y += yoffset; + update.y += yoffset; ret = ioctl(state->fd, FBIO_UPDATE, - (unsigned long)((uintptr_t)area)); + (unsigned long)((uintptr_t)&update)); if (ret < 0) { int errcode = errno; @@ -499,6 +534,7 @@ static void draw_rect(FAR struct fb_state_s *state, int main(int argc, FAR char *argv[]) { FAR const char *fbdev = g_default_fbdev; + int pattern = PATTERN_RECTANGLES; struct fb_state_s state; struct fb_area_s area; int nsteps; @@ -510,20 +546,43 @@ int main(int argc, FAR char *argv[]) int x; int y; int ret; + int opt; - /* There is a single required argument: The path to the framebuffer - * driver. - */ - - if (argc == 2) + while ((opt = getopt(argc, argv, "p:")) != ERROR) { - fbdev = argv[1]; + switch (opt) + { + case 'p': + if (strcmp(optarg, "rectangles") == 0) + { + pattern = PATTERN_RECTANGLES; + } + else if (strcmp(optarg, "smpte") == 0) + { + pattern = PATTERN_SMPTE; + } + else + { + fprintf(stderr, "ERROR: Unknown pattern: %s\n", optarg); + goto usage; + } + + break; + + default: + goto usage; + } } - else if (argc != 1) + + if (optind < argc) { - fprintf(stderr, "ERROR: Single argument required\n"); - fprintf(stderr, "USAGE: %s []\n", argv[0]); - return EXIT_FAILURE; + fbdev = argv[optind++]; + } + + if (optind < argc) + { + fprintf(stderr, "ERROR: Too many framebuffer paths\n"); + goto usage; } /* Open the framebuffer driver */ @@ -652,38 +711,64 @@ int main(int argc, FAR char *argv[]) } } - /* Draw some rectangles */ + /* Draw the selected pattern */ state.act_fbmem = state.fbmem; - nsteps = 2 * (NCOLORS - 1) + 1; - xstep = state.vinfo.xres / nsteps; - ystep = state.vinfo.yres / nsteps; - width = state.vinfo.xres; - height = state.vinfo.yres; - - for (x = 0, y = 0, color = 0; - color < NCOLORS; - x += xstep, y += ystep, color++) + if (pattern == PATTERN_SMPTE) { - area.x = x; - area.y = y; - area.w = width; - area.h = height; - - printf("%2d: (%3d,%3d) (%3d,%3d)\n", - color, area.x, area.y, area.w, area.h); - - draw_rect(&state, &area, color); - usleep(500 * 1000); - - width -= (2 * xstep); - height -= (2 * ystep); - - /* double buffer mode */ - - if (state.pinfo.yres_virtual == (state.vinfo.yres * 2)) + for (color = 0; color < NSMPTECOLORS; color++) { - sync_area(&state); + area.x = color * state.vinfo.xres / NSMPTECOLORS; + area.y = 0; + area.w = (color + 1) * state.vinfo.xres / NSMPTECOLORS - + area.x; + area.h = state.vinfo.yres; + + draw_rect(&state, &area, g_smpte_rgb24[color], + g_smpte_rgb16[color], g_smpte_rgb8[color], + g_smpte_mono[color]); + } + + area.x = 0; + area.y = 0; + area.w = state.vinfo.xres; + area.h = state.vinfo.yres; + present_area(&state, &area); + } + else + { + nsteps = 2 * (NRECTCOLORS - 1) + 1; + xstep = state.vinfo.xres / nsteps; + ystep = state.vinfo.yres / nsteps; + width = state.vinfo.xres; + height = state.vinfo.yres; + + for (x = 0, y = 0, color = 0; + color < NRECTCOLORS; + x += xstep, y += ystep, color++) + { + area.x = x; + area.y = y; + area.w = width; + area.h = height; + + printf("%2d: (%3d,%3d) (%3d,%3d)\n", + color, area.x, area.y, area.w, area.h); + + draw_rect(&state, &area, g_rgb24[color], + g_rgb16[color], g_rgb8[color], color & 1); + present_area(&state, &area); + usleep(500 * 1000); + + width -= (2 * xstep); + height -= (2 * ystep); + + /* double buffer mode */ + + if (state.pinfo.yres_virtual == (state.vinfo.yres * 2)) + { + sync_area(&state); + } } } @@ -693,4 +778,10 @@ out: munmap(state.fbmem, state.pinfo.fblen); close(state.fd); return ret; + +usage: + fprintf(stderr, + "USAGE: %s [-p rectangles|smpte] []\n", + argv[0]); + return EXIT_FAILURE; }