7575#define BANNER_MARGIN_BOTTOM 75L
7676#define BUFFER_SEC 1
7777#define DEFAULT_FORMAT "1920:1080:24:UYVY"
78- #define EPS_PLUS_1 1.0001
7978#define FONT_HEIGHT 108
8079#define MOD_NAME "[testcard2] "
80+ #define RECT1_BASE_STEP_PX 6 ///< rounded up to dest pixfmt block size
81+ #define RECT1_SIZE_PX (13 * PIX_BLOCK_LCM) ///< square edge; multiple of LCM of block pixels for supported formats
82+ #define RECT2_BASE_STEP_PX 12 ///< rounded up to dest pixfmt block size
83+ #define RECT2_FILL_COLOR 0xFFFF00AAU ///< RGBA purple (R=AA G=00 B=FF A=FF); rect1 fill is implicit black from zero-init
84+ #define RECT2_SIZE_PX (4 * PIX_BLOCK_LCM) ///< square edge; multiple of LCM of block pixels for supported formats
85+ #define RECT2_Y_STEP_PX 9 ///< vertical step; no block-alignment needed (lines independent)
8186
8287#ifdef HAVE_LIBSDL_TTF
8388#ifdef _WIN32
@@ -171,10 +176,6 @@ static int vidcap_testcard2_init(struct vidcap_params *params, void **state)
171176 break ;
172177 }
173178 s -> desc .width = atoi (tmp );
174- if (s -> desc .width % 2 != 0 ) {
175- log_msg (LOG_LEVEL_ERROR , "Width must be multiple of 2.\n" );
176- break ;
177- }
178179 if (!(tmp = strtok_r (NULL , ":" , & save_ptr ))) {
179180 log_msg (LOG_LEVEL_ERROR , "Missing height for testcard\n" );
180181 break ;
@@ -192,6 +193,13 @@ static int vidcap_testcard2_init(struct vidcap_params *params, void **state)
192193 }
193194 s -> desc .color_spec = get_codec_from_name (tmp );
194195
196+ int block_px = get_pf_block_pixels (s -> desc .color_spec );
197+ if (s -> desc .width % block_px != 0 ) {
198+ log_msg (LOG_LEVEL_ERROR , "Width must be a multiple of %d for codec %s.\n" ,
199+ block_px , get_codec_name (s -> desc .color_spec ));
200+ break ;
201+ }
202+
195203 ret = VIDCAP_INIT_OK ;
196204 } while (0 );
197205 free (fmt );
@@ -282,10 +290,33 @@ static void vidcap_testcard2_done(void *state)
282290 free (s );
283291}
284292
293+ /**
294+ * Fill the rectangle inside the frame by repeatedly memcpy-ing a
295+ * pre-converted pixel-format block (block_size bytes, block_px pixels
296+ * wide) across each row. Assumes the rectangle's x is aligned to a
297+ * block boundary; partial blocks at the right edge (when w/h has been
298+ * clipped) are skipped, so up to block_px-1 pixels of background may
299+ * show through on the frame following a bounce off the right wall.
300+ */
301+ static void draw_rect_to_buffer (unsigned char * frame , long linesize ,
302+ const struct testcard_rect * r ,
303+ const unsigned char * block , int block_px , int block_size )
304+ {
305+ // Calculate initial pointer location of corner of rectangle (aligned to the block size)
306+ unsigned char * ptr = frame + (long ) r -> y * linesize + (long ) (r -> x / block_px ) * block_size ;
307+ // Copy in the pixels from the block
308+ for (int y = 0 ; y < r -> h ; ++ y ) {
309+ for (int x = 0 ; x < r -> w / block_px ; ++ x ) {
310+ memcpy (ptr + block_size * x , block , block_size );
311+ }
312+ ptr += linesize ;
313+ }
314+ }
315+
285316/**
286317 * Only text banner is rendered in RGBA, other elements (background, squares) are already
287- * converted to destination color space. Keep in mind that the regions should be aligned
288- * to 6 (v210 block size), won't work for R12L
318+ * converted to destination color space. Overlay regions and motion steps are aligned to
319+ * the destination pixel format's block size (e.g. 6 pixels for v210, 8 for R12L).
289320 */
290321void * vidcap_testcard2_thread (void * arg )
291322{
@@ -295,11 +326,20 @@ void * vidcap_testcard2_thread(void *arg)
295326 s = (struct testcard_state2 * )arg ;
296327 struct timeval next_frame_time = { 0 };
297328 srand (time (NULL ));
298- int prev_x1 = rand () % ((s -> desc .width - 300 ) / 6 ) * 6 ;
299- int prev_y1 = rand () % ((s -> desc .height - 300 ) / 6 ) * 6 ;
329+ const int block_px = get_pf_block_pixels (s -> desc .color_spec );
330+ const int block_size = get_pf_block_bytes (s -> desc .color_spec );
331+ const int step1 = (RECT1_BASE_STEP_PX + block_px - 1 ) / block_px * block_px ;
332+ const int step2 = (RECT2_BASE_STEP_PX + block_px - 1 ) / block_px * block_px ;
333+ // Generate the initial position of the rectangles
334+ struct testcard_rect rect1 = {
335+ .x = rand () % ((s -> desc .width - RECT1_SIZE_PX ) / block_px ) * block_px ,
336+ .y = rand () % ((s -> desc .height - RECT1_SIZE_PX ) / block_px ) * block_px ,
337+ };
300338 int down1 = rand () % 2 , right1 = rand () % 2 ;
301- int prev_x2 = rand () % ((s -> desc .width - 96 ) / 6 ) * 6 ;
302- int prev_y2 = rand () % ((s -> desc .height - 96 ) / 6 ) * 6 ;
339+ struct testcard_rect rect2 = {
340+ .x = rand () % ((s -> desc .width - RECT2_SIZE_PX ) / block_px ) * block_px ,
341+ .y = rand () % ((s -> desc .height - RECT2_SIZE_PX ) / block_px ) * block_px ,
342+ };
303343 int down2 = rand () % 2 , right2 = rand () % 2 ;
304344
305345 int stat_count_prev = 0 ;
@@ -330,59 +370,40 @@ void * vidcap_testcard2_thread(void *arg)
330370 }
331371
332372#endif
333- /// @note R12l has pixel block size 8 pixels, so the below won't work for that pixfmt
334- unsigned char square_cols [2 ][48 ];
335- uint32_t src [6 + MAX_PADDING ] = { 0 };
336- testcard_convert_buffer (RGBA , s -> desc .color_spec , square_cols [0 ], (unsigned char * ) src , 6 , 1 );
337- for (int i = 0 ; i < 6 ; ++ i ) src [i ] = 0xffff00aa ;
338- testcard_convert_buffer (RGBA , s -> desc .color_spec , square_cols [1 ], (unsigned char * ) src , 6 , 1 );
339-
340- ptrdiff_t block_size = 6 * EPS_PLUS_1 * get_bpp (s -> desc .color_spec );
373+ // Create colour lookups for rectangles.
374+ unsigned char square_cols [2 ][MAX_PADDING ];
375+ uint32_t src [PIX_BLOCK_LCM + MAX_PADDING ] = { 0 };
376+ assert ((size_t ) block_size <= sizeof square_cols [0 ]);
377+ // Fill the rectangle colour lookups with black and RECT2_FILL_COLOR
378+ testcard_convert_buffer (RGBA , s -> desc .color_spec , square_cols [0 ], (unsigned char * ) src , block_px , 1 );
379+ for (int i = 0 ; i < block_px ; ++ i ) src [i ] = RECT2_FILL_COLOR ;
380+ testcard_convert_buffer (RGBA , s -> desc .color_spec , square_cols [1 ], (unsigned char * ) src , block_px , 1 );
381+
382+ const size_t data_len = vc_get_datalen (s -> desc .width , s -> desc .height , s -> desc .color_spec );
383+ const long linesize = vc_get_linesize (s -> desc .width , s -> desc .color_spec );
341384
342385 while (!s -> should_exit )
343386 {
344- size_t data_len = vc_get_datalen (s -> desc .width , s -> desc .height , s -> desc .color_spec );
387+ // Each iteration allocates a fresh buffer; ownership is handed
388+ // off to s->data and freed by the consumer via vf_data_deleter.
345389 unsigned char * tmp = malloc (data_len );
390+
391+ // Copy in the pre-computed bars background
346392 memcpy (tmp , s -> bg , data_len );
347393
348- struct testcard_rect r ;
349- r .w = 300 ;
350- r .h = 300 ;
351- r .x = prev_x1 + (right1 ? 1 : -1 ) * 6 ;
352- r .y = prev_y1 + (down1 ? 1 : -1 ) * 6 ;
353- if (r .x < 0 ) { right1 = 1 ; r .x = 0 ; }
354- if (r .y < 0 ) { down1 = 1 ; r .y = 0 ; }
355- if ((unsigned int ) r .x + r .w > s -> desc .width ) { right1 = 0 ; r .w = s -> desc .width - r .x ; }
356- if ((unsigned int ) r .y + r .h > s -> desc .height ) { down1 = 0 ; r .h = s -> desc .height - r .y ; }
357- prev_x1 = r .x ;
358- prev_y1 = r .y ;
359-
360- unsigned char * ptr = tmp + r .y * vc_get_linesize (s -> desc .width , s -> desc .color_spec ) + (int ) (r .x * EPS_PLUS_1 * get_bpp (s -> desc .color_spec ));
361- for (int y = 0 ; y < r .h ; ++ y ) {
362- for (int x = 0 ; x < r .w / 6 ; x += 1 ) {
363- memcpy (ptr + block_size * x , square_cols [0 ], block_size );
364- }
365- ptr += vc_get_linesize (s -> desc .width , s -> desc .color_spec );
366- }
394+ // Reset rect size
395+ rect1 .w = RECT1_SIZE_PX ;
396+ rect1 .h = RECT1_SIZE_PX ;
397+ testcard_rect_move (& rect1 , & right1 , & down1 , step1 , step1 ,
398+ s -> desc .width , s -> desc .height );
399+ draw_rect_to_buffer (tmp , linesize , & rect1 , square_cols [0 ], block_px , block_size );
367400
368- r .w = 96 ;
369- r .h = 96 ;
370- r .x = prev_x2 + (right2 ? 1 : -1 ) * 12 ;
371- r .y = prev_y2 + (down2 ? 1 : -1 ) * 9 ;
372- if (r .x < 0 ) { right2 = 1 ; r .x = 0 ; }
373- if (r .y < 0 ) { down2 = 1 ; r .y = 0 ; }
374- if ((unsigned int ) r .x + r .w > s -> desc .width ) { right2 = 0 ; r .w = s -> desc .width - r .x ; }
375- if ((unsigned int ) r .y + r .h > s -> desc .height ) { down2 = 0 ; r .h = s -> desc .height - r .y ; }
376- prev_x2 = r .x ;
377- prev_y2 = r .y ;
378-
379- ptr = tmp + (long ) r .y * vc_get_linesize (s -> desc .width , s -> desc .color_spec ) + (long ) (r .x * EPS_PLUS_1 * get_bpp (s -> desc .color_spec ));
380- for (int y = 0 ; y < r .h ; ++ y ) {
381- for (int x = 0 ; x < r .w / 6 ; x += 1 ) {
382- memcpy (ptr + block_size * x , square_cols [1 ], block_size );
383- }
384- ptr += vc_get_linesize (s -> desc .width , s -> desc .color_spec );
385- }
401+ // Reset rect size
402+ rect2 .w = RECT2_SIZE_PX ;
403+ rect2 .h = RECT2_SIZE_PX ;
404+ testcard_rect_move (& rect2 , & right2 , & down2 , step2 , RECT2_Y_STEP_PX ,
405+ s -> desc .width , s -> desc .height );
406+ draw_rect_to_buffer (tmp , linesize , & rect2 , square_cols [1 ], block_px , block_size );
386407
387408#ifdef HAVE_LIBSDL_TTF
388409 memset (banner , 0xFF , 4L * s -> desc .width * BANNER_HEIGHT );
@@ -408,7 +429,7 @@ void * vidcap_testcard2_thread(void *arg)
408429 d ++ ;
409430 }
410431 }
411- testcard_convert_buffer (RGBA , s -> desc .color_spec , tmp + (s -> desc .height - BANNER_MARGIN_BOTTOM - BANNER_HEIGHT ) * vc_get_linesize ( s -> desc . width , s -> desc . color_spec ) , (unsigned char * ) banner , s -> desc .width , BANNER_HEIGHT );
432+ testcard_convert_buffer (RGBA , s -> desc .color_spec , tmp + (s -> desc .height - BANNER_MARGIN_BOTTOM - BANNER_HEIGHT ) * linesize , (unsigned char * ) banner , s -> desc .width , BANNER_HEIGHT );
412433 SDL_FreeSurface (text );
413434#endif
414435
0 commit comments