2014-08-25 08:02:14 -03:00
/*
* vivid - tpg . c - Test Pattern Generator
*
* Note : gen_twopix and tpg_gen_text are based on code from vivi . c . See the
* vivi . c source for the copyright information of those functions .
*
* Copyright 2014 Cisco Systems , Inc . and / or its affiliates . All rights reserved .
*
* This program is free software ; you may redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License .
*
* 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 "vivid-tpg.h"
/* Must remain in sync with enum tpg_pattern */
const char * const tpg_pattern_strings [ ] = {
" 75% Colorbar " ,
" 100% Colorbar " ,
" CSC Colorbar " ,
" Horizontal 100% Colorbar " ,
" 100% Color Squares " ,
" 100% Black " ,
" 100% White " ,
" 100% Red " ,
" 100% Green " ,
" 100% Blue " ,
" 16x16 Checkers " ,
" 1x1 Checkers " ,
" Alternating Hor Lines " ,
" Alternating Vert Lines " ,
" One Pixel Wide Cross " ,
" Two Pixels Wide Cross " ,
" Ten Pixels Wide Cross " ,
" Gray Ramp " ,
" Noise " ,
NULL
} ;
/* Must remain in sync with enum tpg_aspect */
const char * const tpg_aspect_strings [ ] = {
" Source Width x Height " ,
" 4x3 " ,
" 14x9 " ,
" 16x9 " ,
" 16x9 Anamorphic " ,
NULL
} ;
/*
* Sine table : sin [ 0 ] = 127 * sin ( - 180 degrees )
* sin [ 128 ] = 127 * sin ( 0 degrees )
* sin [ 256 ] = 127 * sin ( 180 degrees )
*/
static const s8 sin [ 257 ] = {
0 , - 4 , - 7 , - 11 , - 13 , - 18 , - 20 , - 22 , - 26 , - 29 , - 33 , - 35 , - 37 , - 41 , - 43 , - 48 ,
- 50 , - 52 , - 56 , - 58 , - 62 , - 63 , - 65 , - 69 , - 71 , - 75 , - 76 , - 78 , - 82 , - 83 , - 87 , - 88 ,
- 90 , - 93 , - 94 , - 97 , - 99 , - 101 , - 103 , - 104 , - 107 , - 108 , - 110 , - 111 , - 112 , - 114 , - 115 , - 117 ,
- 118 , - 119 , - 120 , - 121 , - 122 , - 123 , - 123 , - 124 , - 125 , - 125 , - 126 , - 126 , - 127 , - 127 , - 127 , - 127 ,
- 127 , - 127 , - 127 , - 127 , - 126 , - 126 , - 125 , - 125 , - 124 , - 124 , - 123 , - 122 , - 121 , - 120 , - 119 , - 118 ,
- 117 , - 116 , - 114 , - 113 , - 111 , - 110 , - 109 , - 107 , - 105 , - 103 , - 101 , - 100 , - 97 , - 96 , - 93 , - 91 ,
- 90 , - 87 , - 85 , - 82 , - 80 , - 76 , - 75 , - 73 , - 69 , - 67 , - 63 , - 62 , - 60 , - 56 , - 54 , - 50 ,
- 48 , - 46 , - 41 , - 39 , - 35 , - 33 , - 31 , - 26 , - 24 , - 20 , - 18 , - 15 , - 11 , - 9 , - 4 , - 2 ,
0 , 2 , 4 , 9 , 11 , 15 , 18 , 20 , 24 , 26 , 31 , 33 , 35 , 39 , 41 , 46 ,
48 , 50 , 54 , 56 , 60 , 62 , 64 , 67 , 69 , 73 , 75 , 76 , 80 , 82 , 85 , 87 ,
90 , 91 , 93 , 96 , 97 , 100 , 101 , 103 , 105 , 107 , 109 , 110 , 111 , 113 , 114 , 116 ,
117 , 118 , 119 , 120 , 121 , 122 , 123 , 124 , 124 , 125 , 125 , 126 , 126 , 127 , 127 , 127 ,
127 , 127 , 127 , 127 , 127 , 126 , 126 , 125 , 125 , 124 , 123 , 123 , 122 , 121 , 120 , 119 ,
118 , 117 , 115 , 114 , 112 , 111 , 110 , 108 , 107 , 104 , 103 , 101 , 99 , 97 , 94 , 93 ,
90 , 88 , 87 , 83 , 82 , 78 , 76 , 75 , 71 , 69 , 65 , 64 , 62 , 58 , 56 , 52 ,
50 , 48 , 43 , 41 , 37 , 35 , 33 , 29 , 26 , 22 , 20 , 18 , 13 , 11 , 7 , 4 ,
0 ,
} ;
# define cos(idx) sin[((idx) + 64) % sizeof(sin)]
/* Global font descriptor */
static const u8 * font8x16 ;
void tpg_set_font ( const u8 * f )
{
font8x16 = f ;
}
void tpg_init ( struct tpg_data * tpg , unsigned w , unsigned h )
{
memset ( tpg , 0 , sizeof ( * tpg ) ) ;
tpg - > scaled_width = tpg - > src_width = w ;
tpg - > src_height = tpg - > buf_height = h ;
tpg - > crop . width = tpg - > compose . width = w ;
tpg - > crop . height = tpg - > compose . height = h ;
tpg - > recalc_colors = true ;
tpg - > recalc_square_border = true ;
tpg - > brightness = 128 ;
tpg - > contrast = 128 ;
tpg - > saturation = 128 ;
tpg - > hue = 0 ;
tpg - > mv_hor_mode = TPG_MOVE_NONE ;
tpg - > mv_vert_mode = TPG_MOVE_NONE ;
tpg - > field = V4L2_FIELD_NONE ;
tpg_s_fourcc ( tpg , V4L2_PIX_FMT_RGB24 ) ;
tpg - > colorspace = V4L2_COLORSPACE_SRGB ;
tpg - > perc_fill = 100 ;
}
int tpg_alloc ( struct tpg_data * tpg , unsigned max_w )
{
unsigned pat ;
unsigned plane ;
tpg - > max_line_width = max_w ;
for ( pat = 0 ; pat < TPG_MAX_PAT_LINES ; pat + + ) {
for ( plane = 0 ; plane < TPG_MAX_PLANES ; plane + + ) {
unsigned pixelsz = plane ? 1 : 4 ;
tpg - > lines [ pat ] [ plane ] = vzalloc ( max_w * 2 * pixelsz ) ;
if ( ! tpg - > lines [ pat ] [ plane ] )
return - ENOMEM ;
}
}
for ( plane = 0 ; plane < TPG_MAX_PLANES ; plane + + ) {
unsigned pixelsz = plane ? 1 : 4 ;
tpg - > contrast_line [ plane ] = vzalloc ( max_w * pixelsz ) ;
if ( ! tpg - > contrast_line [ plane ] )
return - ENOMEM ;
tpg - > black_line [ plane ] = vzalloc ( max_w * pixelsz ) ;
if ( ! tpg - > black_line [ plane ] )
return - ENOMEM ;
tpg - > random_line [ plane ] = vzalloc ( max_w * pixelsz ) ;
if ( ! tpg - > random_line [ plane ] )
return - ENOMEM ;
}
return 0 ;
}
void tpg_free ( struct tpg_data * tpg )
{
unsigned pat ;
unsigned plane ;
for ( pat = 0 ; pat < TPG_MAX_PAT_LINES ; pat + + )
for ( plane = 0 ; plane < TPG_MAX_PLANES ; plane + + ) {
vfree ( tpg - > lines [ pat ] [ plane ] ) ;
tpg - > lines [ pat ] [ plane ] = NULL ;
}
for ( plane = 0 ; plane < TPG_MAX_PLANES ; plane + + ) {
vfree ( tpg - > contrast_line [ plane ] ) ;
vfree ( tpg - > black_line [ plane ] ) ;
vfree ( tpg - > random_line [ plane ] ) ;
tpg - > contrast_line [ plane ] = NULL ;
tpg - > black_line [ plane ] = NULL ;
tpg - > random_line [ plane ] = NULL ;
}
}
bool tpg_s_fourcc ( struct tpg_data * tpg , u32 fourcc )
{
tpg - > fourcc = fourcc ;
tpg - > planes = 1 ;
tpg - > recalc_colors = true ;
switch ( fourcc ) {
case V4L2_PIX_FMT_RGB565 :
case V4L2_PIX_FMT_RGB565X :
case V4L2_PIX_FMT_RGB555 :
case V4L2_PIX_FMT_XRGB555 :
case V4L2_PIX_FMT_ARGB555 :
case V4L2_PIX_FMT_RGB555X :
case V4L2_PIX_FMT_RGB24 :
case V4L2_PIX_FMT_BGR24 :
case V4L2_PIX_FMT_RGB32 :
case V4L2_PIX_FMT_BGR32 :
case V4L2_PIX_FMT_XRGB32 :
case V4L2_PIX_FMT_XBGR32 :
case V4L2_PIX_FMT_ARGB32 :
case V4L2_PIX_FMT_ABGR32 :
2014-09-03 15:53:45 -03:00
tpg - > is_yuv = false ;
2014-08-25 08:02:14 -03:00
break ;
case V4L2_PIX_FMT_NV16M :
case V4L2_PIX_FMT_NV61M :
tpg - > planes = 2 ;
/* fall-through */
case V4L2_PIX_FMT_YUYV :
case V4L2_PIX_FMT_UYVY :
case V4L2_PIX_FMT_YVYU :
case V4L2_PIX_FMT_VYUY :
2014-09-03 15:53:45 -03:00
tpg - > is_yuv = true ;
2014-08-25 08:02:14 -03:00
break ;
default :
return false ;
}
switch ( fourcc ) {
case V4L2_PIX_FMT_RGB565 :
case V4L2_PIX_FMT_RGB565X :
case V4L2_PIX_FMT_RGB555 :
case V4L2_PIX_FMT_XRGB555 :
case V4L2_PIX_FMT_ARGB555 :
case V4L2_PIX_FMT_RGB555X :
case V4L2_PIX_FMT_YUYV :
case V4L2_PIX_FMT_UYVY :
case V4L2_PIX_FMT_YVYU :
case V4L2_PIX_FMT_VYUY :
tpg - > twopixelsize [ 0 ] = 2 * 2 ;
break ;
case V4L2_PIX_FMT_RGB24 :
case V4L2_PIX_FMT_BGR24 :
tpg - > twopixelsize [ 0 ] = 2 * 3 ;
break ;
case V4L2_PIX_FMT_RGB32 :
case V4L2_PIX_FMT_BGR32 :
case V4L2_PIX_FMT_XRGB32 :
case V4L2_PIX_FMT_XBGR32 :
case V4L2_PIX_FMT_ARGB32 :
case V4L2_PIX_FMT_ABGR32 :
tpg - > twopixelsize [ 0 ] = 2 * 4 ;
break ;
case V4L2_PIX_FMT_NV16M :
case V4L2_PIX_FMT_NV61M :
tpg - > twopixelsize [ 0 ] = 2 ;
tpg - > twopixelsize [ 1 ] = 2 ;
break ;
}
return true ;
}
void tpg_s_crop_compose ( struct tpg_data * tpg , const struct v4l2_rect * crop ,
const struct v4l2_rect * compose )
{
tpg - > crop = * crop ;
tpg - > compose = * compose ;
tpg - > scaled_width = ( tpg - > src_width * tpg - > compose . width +
tpg - > crop . width - 1 ) / tpg - > crop . width ;
tpg - > scaled_width & = ~ 1 ;
if ( tpg - > scaled_width > tpg - > max_line_width )
tpg - > scaled_width = tpg - > max_line_width ;
if ( tpg - > scaled_width < 2 )
tpg - > scaled_width = 2 ;
tpg - > recalc_lines = true ;
}
void tpg_reset_source ( struct tpg_data * tpg , unsigned width , unsigned height ,
2014-09-03 10:18:57 -03:00
u32 field )
2014-08-25 08:02:14 -03:00
{
unsigned p ;
tpg - > src_width = width ;
tpg - > src_height = height ;
tpg - > field = field ;
tpg - > buf_height = height ;
if ( V4L2_FIELD_HAS_T_OR_B ( field ) )
tpg - > buf_height / = 2 ;
tpg - > scaled_width = width ;
tpg - > crop . top = tpg - > crop . left = 0 ;
tpg - > crop . width = width ;
tpg - > crop . height = height ;
tpg - > compose . top = tpg - > compose . left = 0 ;
tpg - > compose . width = width ;
tpg - > compose . height = tpg - > buf_height ;
for ( p = 0 ; p < tpg - > planes ; p + + )
tpg - > bytesperline [ p ] = width * tpg - > twopixelsize [ p ] / 2 ;
tpg - > recalc_square_border = true ;
}
static enum tpg_color tpg_get_textbg_color ( struct tpg_data * tpg )
{
switch ( tpg - > pattern ) {
case TPG_PAT_BLACK :
return TPG_COLOR_100_WHITE ;
case TPG_PAT_CSC_COLORBAR :
return TPG_COLOR_CSC_BLACK ;
default :
return TPG_COLOR_100_BLACK ;
}
}
static enum tpg_color tpg_get_textfg_color ( struct tpg_data * tpg )
{
switch ( tpg - > pattern ) {
case TPG_PAT_75_COLORBAR :
case TPG_PAT_CSC_COLORBAR :
return TPG_COLOR_CSC_WHITE ;
case TPG_PAT_BLACK :
return TPG_COLOR_100_BLACK ;
default :
return TPG_COLOR_100_WHITE ;
}
}
static u16 color_to_y ( struct tpg_data * tpg , int r , int g , int b )
{
switch ( tpg - > colorspace ) {
case V4L2_COLORSPACE_SMPTE170M :
case V4L2_COLORSPACE_470_SYSTEM_M :
case V4L2_COLORSPACE_470_SYSTEM_BG :
return ( ( 16829 * r + 33039 * g + 6416 * b + 16 * 32768 ) > > 16 ) + ( 16 < < 4 ) ;
case V4L2_COLORSPACE_SMPTE240M :
return ( ( 11932 * r + 39455 * g + 4897 * b + 16 * 32768 ) > > 16 ) + ( 16 < < 4 ) ;
case V4L2_COLORSPACE_REC709 :
case V4L2_COLORSPACE_SRGB :
default :
return ( ( 11966 * r + 40254 * g + 4064 * b + 16 * 32768 ) > > 16 ) + ( 16 < < 4 ) ;
}
}
static u16 color_to_cb ( struct tpg_data * tpg , int r , int g , int b )
{
switch ( tpg - > colorspace ) {
case V4L2_COLORSPACE_SMPTE170M :
case V4L2_COLORSPACE_470_SYSTEM_M :
case V4L2_COLORSPACE_470_SYSTEM_BG :
return ( ( - 9714 * r - 19070 * g + 28784 * b + 16 * 32768 ) > > 16 ) + ( 128 < < 4 ) ;
case V4L2_COLORSPACE_SMPTE240M :
return ( ( - 6684 * r - 22100 * g + 28784 * b + 16 * 32768 ) > > 16 ) + ( 128 < < 4 ) ;
case V4L2_COLORSPACE_REC709 :
case V4L2_COLORSPACE_SRGB :
default :
return ( ( - 6596 * r - 22189 * g + 28784 * b + 16 * 32768 ) > > 16 ) + ( 128 < < 4 ) ;
}
}
static u16 color_to_cr ( struct tpg_data * tpg , int r , int g , int b )
{
switch ( tpg - > colorspace ) {
case V4L2_COLORSPACE_SMPTE170M :
case V4L2_COLORSPACE_470_SYSTEM_M :
case V4L2_COLORSPACE_470_SYSTEM_BG :
return ( ( 28784 * r - 24103 * g - 4681 * b + 16 * 32768 ) > > 16 ) + ( 128 < < 4 ) ;
case V4L2_COLORSPACE_SMPTE240M :
return ( ( 28784 * r - 25606 * g - 3178 * b + 16 * 32768 ) > > 16 ) + ( 128 < < 4 ) ;
case V4L2_COLORSPACE_REC709 :
case V4L2_COLORSPACE_SRGB :
default :
return ( ( 28784 * r - 26145 * g - 2639 * b + 16 * 32768 ) > > 16 ) + ( 128 < < 4 ) ;
}
}
static u16 ycbcr_to_r ( struct tpg_data * tpg , int y , int cb , int cr )
{
int r ;
y - = 16 < < 4 ;
cb - = 128 < < 4 ;
cr - = 128 < < 4 ;
switch ( tpg - > colorspace ) {
case V4L2_COLORSPACE_SMPTE170M :
case V4L2_COLORSPACE_470_SYSTEM_M :
case V4L2_COLORSPACE_470_SYSTEM_BG :
r = 4769 * y + 6537 * cr ;
break ;
case V4L2_COLORSPACE_SMPTE240M :
r = 4769 * y + 7376 * cr ;
break ;
case V4L2_COLORSPACE_REC709 :
case V4L2_COLORSPACE_SRGB :
default :
r = 4769 * y + 7343 * cr ;
break ;
}
return clamp ( r > > 12 , 0 , 0xff0 ) ;
}
static u16 ycbcr_to_g ( struct tpg_data * tpg , int y , int cb , int cr )
{
int g ;
y - = 16 < < 4 ;
cb - = 128 < < 4 ;
cr - = 128 < < 4 ;
switch ( tpg - > colorspace ) {
case V4L2_COLORSPACE_SMPTE170M :
case V4L2_COLORSPACE_470_SYSTEM_M :
case V4L2_COLORSPACE_470_SYSTEM_BG :
g = 4769 * y - 1605 * cb - 3330 * cr ;
break ;
case V4L2_COLORSPACE_SMPTE240M :
g = 4769 * y - 1055 * cb - 2341 * cr ;
break ;
case V4L2_COLORSPACE_REC709 :
case V4L2_COLORSPACE_SRGB :
default :
g = 4769 * y - 873 * cb - 2183 * cr ;
break ;
}
return clamp ( g > > 12 , 0 , 0xff0 ) ;
}
static u16 ycbcr_to_b ( struct tpg_data * tpg , int y , int cb , int cr )
{
int b ;
y - = 16 < < 4 ;
cb - = 128 < < 4 ;
cr - = 128 < < 4 ;
switch ( tpg - > colorspace ) {
case V4L2_COLORSPACE_SMPTE170M :
case V4L2_COLORSPACE_470_SYSTEM_M :
case V4L2_COLORSPACE_470_SYSTEM_BG :
b = 4769 * y + 7343 * cb ;
break ;
case V4L2_COLORSPACE_SMPTE240M :
b = 4769 * y + 8552 * cb ;
break ;
case V4L2_COLORSPACE_REC709 :
case V4L2_COLORSPACE_SRGB :
default :
b = 4769 * y + 8652 * cb ;
break ;
}
return clamp ( b > > 12 , 0 , 0xff0 ) ;
}
/* precalculate color bar values to speed up rendering */
static void precalculate_color ( struct tpg_data * tpg , int k )
{
int col = k ;
int r = tpg_colors [ col ] . r ;
int g = tpg_colors [ col ] . g ;
int b = tpg_colors [ col ] . b ;
if ( k = = TPG_COLOR_TEXTBG ) {
col = tpg_get_textbg_color ( tpg ) ;
r = tpg_colors [ col ] . r ;
g = tpg_colors [ col ] . g ;
b = tpg_colors [ col ] . b ;
} else if ( k = = TPG_COLOR_TEXTFG ) {
col = tpg_get_textfg_color ( tpg ) ;
r = tpg_colors [ col ] . r ;
g = tpg_colors [ col ] . g ;
b = tpg_colors [ col ] . b ;
} else if ( tpg - > pattern = = TPG_PAT_NOISE ) {
r = g = b = prandom_u32_max ( 256 ) ;
} else if ( k = = TPG_COLOR_RANDOM ) {
r = g = b = tpg - > qual_offset + prandom_u32_max ( 196 ) ;
} else if ( k > = TPG_COLOR_RAMP ) {
r = g = b = k - TPG_COLOR_RAMP ;
}
if ( tpg - > pattern = = TPG_PAT_CSC_COLORBAR & & col < = TPG_COLOR_CSC_BLACK ) {
r = tpg_csc_colors [ tpg - > colorspace ] [ col ] . r ;
g = tpg_csc_colors [ tpg - > colorspace ] [ col ] . g ;
b = tpg_csc_colors [ tpg - > colorspace ] [ col ] . b ;
} else {
r < < = 4 ;
g < < = 4 ;
b < < = 4 ;
}
if ( tpg - > qual = = TPG_QUAL_GRAY )
r = g = b = color_to_y ( tpg , r , g , b ) ;
/*
* The assumption is that the RGB output is always full range ,
* so only if the rgb_range overrides the ' real ' rgb range do
* we need to convert the RGB values .
*
* Currently there is no way of signalling to userspace if you
* are actually giving it limited range RGB ( or full range
* YUV for that matter ) .
*
* Remember that r , g and b are still in the 0 - 0xff0 range .
*/
if ( tpg - > real_rgb_range = = V4L2_DV_RGB_RANGE_LIMITED & &
tpg - > rgb_range = = V4L2_DV_RGB_RANGE_FULL ) {
/*
* Convert from full range ( which is what r , g and b are )
* to limited range ( which is the ' real ' RGB range ) , which
* is then interpreted as full range .
*/
r = ( r * 219 ) / 255 + ( 16 < < 4 ) ;
g = ( g * 219 ) / 255 + ( 16 < < 4 ) ;
b = ( b * 219 ) / 255 + ( 16 < < 4 ) ;
} else if ( tpg - > real_rgb_range ! = V4L2_DV_RGB_RANGE_LIMITED & &
tpg - > rgb_range = = V4L2_DV_RGB_RANGE_LIMITED ) {
/*
* Clamp r , g and b to the limited range and convert to full
* range since that ' s what we deliver .
*/
r = clamp ( r , 16 < < 4 , 235 < < 4 ) ;
g = clamp ( g , 16 < < 4 , 235 < < 4 ) ;
b = clamp ( b , 16 < < 4 , 235 < < 4 ) ;
r = ( r - ( 16 < < 4 ) ) * 255 / 219 ;
g = ( g - ( 16 < < 4 ) ) * 255 / 219 ;
b = ( b - ( 16 < < 4 ) ) * 255 / 219 ;
}
if ( tpg - > brightness ! = 128 | | tpg - > contrast ! = 128 | |
tpg - > saturation ! = 128 | | tpg - > hue ) {
/* Implement these operations */
/* First convert to YCbCr */
int y = color_to_y ( tpg , r , g , b ) ; /* Luma */
int cb = color_to_cb ( tpg , r , g , b ) ; /* Cb */
int cr = color_to_cr ( tpg , r , g , b ) ; /* Cr */
int tmp_cb , tmp_cr ;
y = ( 16 < < 4 ) + ( ( y - ( 16 < < 4 ) ) * tpg - > contrast ) / 128 ;
y + = ( tpg - > brightness < < 4 ) - ( 128 < < 4 ) ;
cb - = 128 < < 4 ;
cr - = 128 < < 4 ;
tmp_cb = ( cb * cos ( 128 + tpg - > hue ) ) / 127 + ( cr * sin [ 128 + tpg - > hue ] ) / 127 ;
tmp_cr = ( cr * cos ( 128 + tpg - > hue ) ) / 127 - ( cb * sin [ 128 + tpg - > hue ] ) / 127 ;
cb = ( 128 < < 4 ) + ( tmp_cb * tpg - > contrast * tpg - > saturation ) / ( 128 * 128 ) ;
cr = ( 128 < < 4 ) + ( tmp_cr * tpg - > contrast * tpg - > saturation ) / ( 128 * 128 ) ;
if ( tpg - > is_yuv ) {
tpg - > colors [ k ] [ 0 ] = clamp ( y > > 4 , 1 , 254 ) ;
tpg - > colors [ k ] [ 1 ] = clamp ( cb > > 4 , 1 , 254 ) ;
tpg - > colors [ k ] [ 2 ] = clamp ( cr > > 4 , 1 , 254 ) ;
return ;
}
r = ycbcr_to_r ( tpg , y , cb , cr ) ;
g = ycbcr_to_g ( tpg , y , cb , cr ) ;
b = ycbcr_to_b ( tpg , y , cb , cr ) ;
}
if ( tpg - > is_yuv ) {
/* Convert to YCbCr */
u16 y = color_to_y ( tpg , r , g , b ) ; /* Luma */
u16 cb = color_to_cb ( tpg , r , g , b ) ; /* Cb */
u16 cr = color_to_cr ( tpg , r , g , b ) ; /* Cr */
tpg - > colors [ k ] [ 0 ] = clamp ( y > > 4 , 1 , 254 ) ;
tpg - > colors [ k ] [ 1 ] = clamp ( cb > > 4 , 1 , 254 ) ;
tpg - > colors [ k ] [ 2 ] = clamp ( cr > > 4 , 1 , 254 ) ;
} else {
switch ( tpg - > fourcc ) {
case V4L2_PIX_FMT_RGB565 :
case V4L2_PIX_FMT_RGB565X :
r > > = 7 ;
g > > = 6 ;
b > > = 7 ;
break ;
case V4L2_PIX_FMT_RGB555 :
case V4L2_PIX_FMT_XRGB555 :
case V4L2_PIX_FMT_ARGB555 :
case V4L2_PIX_FMT_RGB555X :
r > > = 7 ;
g > > = 7 ;
b > > = 7 ;
break ;
default :
r > > = 4 ;
g > > = 4 ;
b > > = 4 ;
break ;
}
tpg - > colors [ k ] [ 0 ] = r ;
tpg - > colors [ k ] [ 1 ] = g ;
tpg - > colors [ k ] [ 2 ] = b ;
}
}
static void tpg_precalculate_colors ( struct tpg_data * tpg )
{
int k ;
for ( k = 0 ; k < TPG_COLOR_MAX ; k + + )
precalculate_color ( tpg , k ) ;
}
/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
static void gen_twopix ( struct tpg_data * tpg ,
u8 buf [ TPG_MAX_PLANES ] [ 8 ] , int color , bool odd )
{
unsigned offset = odd * tpg - > twopixelsize [ 0 ] / 2 ;
u8 alpha = tpg - > alpha_component ;
u8 r_y , g_u , b_v ;
if ( tpg - > alpha_red_only & & color ! = TPG_COLOR_CSC_RED & &
color ! = TPG_COLOR_100_RED & &
color ! = TPG_COLOR_75_RED )
alpha = 0 ;
if ( color = = TPG_COLOR_RANDOM )
precalculate_color ( tpg , color ) ;
r_y = tpg - > colors [ color ] [ 0 ] ; /* R or precalculated Y */
g_u = tpg - > colors [ color ] [ 1 ] ; /* G or precalculated U */
b_v = tpg - > colors [ color ] [ 2 ] ; /* B or precalculated V */
switch ( tpg - > fourcc ) {
case V4L2_PIX_FMT_NV16M :
buf [ 0 ] [ offset ] = r_y ;
buf [ 1 ] [ offset ] = odd ? b_v : g_u ;
break ;
case V4L2_PIX_FMT_NV61M :
buf [ 0 ] [ offset ] = r_y ;
buf [ 1 ] [ offset ] = odd ? g_u : b_v ;
break ;
case V4L2_PIX_FMT_YUYV :
buf [ 0 ] [ offset ] = r_y ;
buf [ 0 ] [ offset + 1 ] = odd ? b_v : g_u ;
break ;
case V4L2_PIX_FMT_UYVY :
buf [ 0 ] [ offset ] = odd ? b_v : g_u ;
buf [ 0 ] [ offset + 1 ] = r_y ;
break ;
case V4L2_PIX_FMT_YVYU :
buf [ 0 ] [ offset ] = r_y ;
buf [ 0 ] [ offset + 1 ] = odd ? g_u : b_v ;
break ;
case V4L2_PIX_FMT_VYUY :
buf [ 0 ] [ offset ] = odd ? g_u : b_v ;
buf [ 0 ] [ offset + 1 ] = r_y ;
break ;
case V4L2_PIX_FMT_RGB565 :
buf [ 0 ] [ offset ] = ( g_u < < 5 ) | b_v ;
buf [ 0 ] [ offset + 1 ] = ( r_y < < 3 ) | ( g_u > > 3 ) ;
break ;
case V4L2_PIX_FMT_RGB565X :
buf [ 0 ] [ offset ] = ( r_y < < 3 ) | ( g_u > > 3 ) ;
buf [ 0 ] [ offset + 1 ] = ( g_u < < 5 ) | b_v ;
break ;
case V4L2_PIX_FMT_RGB555 :
case V4L2_PIX_FMT_XRGB555 :
alpha = 0 ;
/* fall through */
case V4L2_PIX_FMT_ARGB555 :
buf [ 0 ] [ offset ] = ( g_u < < 5 ) | b_v ;
buf [ 0 ] [ offset + 1 ] = ( alpha & 0x80 ) | ( r_y < < 2 ) | ( g_u > > 3 ) ;
break ;
case V4L2_PIX_FMT_RGB555X :
buf [ 0 ] [ offset ] = ( alpha & 0x80 ) | ( r_y < < 2 ) | ( g_u > > 3 ) ;
buf [ 0 ] [ offset + 1 ] = ( g_u < < 5 ) | b_v ;
break ;
case V4L2_PIX_FMT_RGB24 :
buf [ 0 ] [ offset ] = r_y ;
buf [ 0 ] [ offset + 1 ] = g_u ;
buf [ 0 ] [ offset + 2 ] = b_v ;
break ;
case V4L2_PIX_FMT_BGR24 :
buf [ 0 ] [ offset ] = b_v ;
buf [ 0 ] [ offset + 1 ] = g_u ;
buf [ 0 ] [ offset + 2 ] = r_y ;
break ;
case V4L2_PIX_FMT_RGB32 :
case V4L2_PIX_FMT_XRGB32 :
alpha = 0 ;
/* fall through */
case V4L2_PIX_FMT_ARGB32 :
buf [ 0 ] [ offset ] = alpha ;
buf [ 0 ] [ offset + 1 ] = r_y ;
buf [ 0 ] [ offset + 2 ] = g_u ;
buf [ 0 ] [ offset + 3 ] = b_v ;
break ;
case V4L2_PIX_FMT_BGR32 :
case V4L2_PIX_FMT_XBGR32 :
alpha = 0 ;
/* fall through */
case V4L2_PIX_FMT_ABGR32 :
buf [ 0 ] [ offset ] = b_v ;
buf [ 0 ] [ offset + 1 ] = g_u ;
buf [ 0 ] [ offset + 2 ] = r_y ;
buf [ 0 ] [ offset + 3 ] = alpha ;
break ;
}
}
/* Return how many pattern lines are used by the current pattern. */
static unsigned tpg_get_pat_lines ( struct tpg_data * tpg )
{
switch ( tpg - > pattern ) {
case TPG_PAT_CHECKERS_16X16 :
case TPG_PAT_CHECKERS_1X1 :
case TPG_PAT_ALTERNATING_HLINES :
case TPG_PAT_CROSS_1_PIXEL :
case TPG_PAT_CROSS_2_PIXELS :
case TPG_PAT_CROSS_10_PIXELS :
return 2 ;
case TPG_PAT_100_COLORSQUARES :
case TPG_PAT_100_HCOLORBAR :
return 8 ;
default :
return 1 ;
}
}
/* Which pattern line should be used for the given frame line. */
static unsigned tpg_get_pat_line ( struct tpg_data * tpg , unsigned line )
{
switch ( tpg - > pattern ) {
case TPG_PAT_CHECKERS_16X16 :
return ( line > > 4 ) & 1 ;
case TPG_PAT_CHECKERS_1X1 :
case TPG_PAT_ALTERNATING_HLINES :
return line & 1 ;
case TPG_PAT_100_COLORSQUARES :
case TPG_PAT_100_HCOLORBAR :
return ( line * 8 ) / tpg - > src_height ;
case TPG_PAT_CROSS_1_PIXEL :
return line = = tpg - > src_height / 2 ;
case TPG_PAT_CROSS_2_PIXELS :
return ( line + 1 ) / 2 = = tpg - > src_height / 4 ;
case TPG_PAT_CROSS_10_PIXELS :
return ( line + 10 ) / 20 = = tpg - > src_height / 40 ;
default :
return 0 ;
}
}
/*
* Which color should be used for the given pattern line and X coordinate .
* Note : x is in the range 0 to 2 * tpg - > src_width .
*/
static enum tpg_color tpg_get_color ( struct tpg_data * tpg , unsigned pat_line , unsigned x )
{
/* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code
should be modified */
static const enum tpg_color bars [ 3 ] [ 8 ] = {
/* Standard ITU-R 75% color bar sequence */
{ TPG_COLOR_CSC_WHITE , TPG_COLOR_75_YELLOW ,
TPG_COLOR_75_CYAN , TPG_COLOR_75_GREEN ,
TPG_COLOR_75_MAGENTA , TPG_COLOR_75_RED ,
TPG_COLOR_75_BLUE , TPG_COLOR_100_BLACK , } ,
/* Standard ITU-R 100% color bar sequence */
{ TPG_COLOR_100_WHITE , TPG_COLOR_100_YELLOW ,
TPG_COLOR_100_CYAN , TPG_COLOR_100_GREEN ,
TPG_COLOR_100_MAGENTA , TPG_COLOR_100_RED ,
TPG_COLOR_100_BLUE , TPG_COLOR_100_BLACK , } ,
/* Color bar sequence suitable to test CSC */
{ TPG_COLOR_CSC_WHITE , TPG_COLOR_CSC_YELLOW ,
TPG_COLOR_CSC_CYAN , TPG_COLOR_CSC_GREEN ,
TPG_COLOR_CSC_MAGENTA , TPG_COLOR_CSC_RED ,
TPG_COLOR_CSC_BLUE , TPG_COLOR_CSC_BLACK , } ,
} ;
switch ( tpg - > pattern ) {
case TPG_PAT_75_COLORBAR :
case TPG_PAT_100_COLORBAR :
case TPG_PAT_CSC_COLORBAR :
return bars [ tpg - > pattern ] [ ( ( x * 8 ) / tpg - > src_width ) % 8 ] ;
case TPG_PAT_100_COLORSQUARES :
return bars [ 1 ] [ ( pat_line + ( x * 8 ) / tpg - > src_width ) % 8 ] ;
case TPG_PAT_100_HCOLORBAR :
return bars [ 1 ] [ pat_line ] ;
case TPG_PAT_BLACK :
return TPG_COLOR_100_BLACK ;
case TPG_PAT_WHITE :
return TPG_COLOR_100_WHITE ;
case TPG_PAT_RED :
return TPG_COLOR_100_RED ;
case TPG_PAT_GREEN :
return TPG_COLOR_100_GREEN ;
case TPG_PAT_BLUE :
return TPG_COLOR_100_BLUE ;
case TPG_PAT_CHECKERS_16X16 :
return ( ( ( x > > 4 ) & 1 ) ^ ( pat_line & 1 ) ) ?
TPG_COLOR_100_BLACK : TPG_COLOR_100_WHITE ;
case TPG_PAT_CHECKERS_1X1 :
return ( ( x & 1 ) ^ ( pat_line & 1 ) ) ?
TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK ;
case TPG_PAT_ALTERNATING_HLINES :
return pat_line ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK ;
case TPG_PAT_ALTERNATING_VLINES :
return ( x & 1 ) ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK ;
case TPG_PAT_CROSS_1_PIXEL :
if ( pat_line | | ( x % tpg - > src_width ) = = tpg - > src_width / 2 )
return TPG_COLOR_100_BLACK ;
return TPG_COLOR_100_WHITE ;
case TPG_PAT_CROSS_2_PIXELS :
if ( pat_line | | ( ( x % tpg - > src_width ) + 1 ) / 2 = = tpg - > src_width / 4 )
return TPG_COLOR_100_BLACK ;
return TPG_COLOR_100_WHITE ;
case TPG_PAT_CROSS_10_PIXELS :
if ( pat_line | | ( ( x % tpg - > src_width ) + 10 ) / 20 = = tpg - > src_width / 40 )
return TPG_COLOR_100_BLACK ;
return TPG_COLOR_100_WHITE ;
case TPG_PAT_GRAY_RAMP :
return TPG_COLOR_RAMP + ( ( x % tpg - > src_width ) * 256 ) / tpg - > src_width ;
default :
return TPG_COLOR_100_RED ;
}
}
/*
* Given the pixel aspect ratio and video aspect ratio calculate the
* coordinates of a centered square and the coordinates of the border of
* the active video area . The coordinates are relative to the source
* frame rectangle .
*/
static void tpg_calculate_square_border ( struct tpg_data * tpg )
{
unsigned w = tpg - > src_width ;
unsigned h = tpg - > src_height ;
unsigned sq_w , sq_h ;
sq_w = ( w * 2 / 5 ) & ~ 1 ;
if ( ( ( w - sq_w ) / 2 ) & 1 )
sq_w + = 2 ;
sq_h = sq_w ;
tpg - > square . width = sq_w ;
if ( tpg - > vid_aspect = = TPG_VIDEO_ASPECT_16X9_ANAMORPHIC ) {
unsigned ana_sq_w = ( sq_w / 4 ) * 3 ;
if ( ( ( w - ana_sq_w ) / 2 ) & 1 )
ana_sq_w + = 2 ;
tpg - > square . width = ana_sq_w ;
}
tpg - > square . left = ( w - tpg - > square . width ) / 2 ;
if ( tpg - > pix_aspect = = TPG_PIXEL_ASPECT_NTSC )
sq_h = sq_w * 10 / 11 ;
else if ( tpg - > pix_aspect = = TPG_PIXEL_ASPECT_PAL )
sq_h = sq_w * 59 / 54 ;
tpg - > square . height = sq_h ;
tpg - > square . top = ( h - sq_h ) / 2 ;
tpg - > border . left = 0 ;
tpg - > border . width = w ;
tpg - > border . top = 0 ;
tpg - > border . height = h ;
switch ( tpg - > vid_aspect ) {
case TPG_VIDEO_ASPECT_4X3 :
if ( tpg - > pix_aspect )
return ;
if ( 3 * w > = 4 * h ) {
tpg - > border . width = ( ( 4 * h ) / 3 ) & ~ 1 ;
if ( ( ( w - tpg - > border . width ) / 2 ) & ~ 1 )
tpg - > border . width - = 2 ;
tpg - > border . left = ( w - tpg - > border . width ) / 2 ;
break ;
}
tpg - > border . height = ( ( 3 * w ) / 4 ) & ~ 1 ;
tpg - > border . top = ( h - tpg - > border . height ) / 2 ;
break ;
case TPG_VIDEO_ASPECT_14X9_CENTRE :
if ( tpg - > pix_aspect ) {
tpg - > border . height = tpg - > pix_aspect = = TPG_PIXEL_ASPECT_NTSC ? 420 : 506 ;
tpg - > border . top = ( h - tpg - > border . height ) / 2 ;
break ;
}
if ( 9 * w > = 14 * h ) {
tpg - > border . width = ( ( 14 * h ) / 9 ) & ~ 1 ;
if ( ( ( w - tpg - > border . width ) / 2 ) & ~ 1 )
tpg - > border . width - = 2 ;
tpg - > border . left = ( w - tpg - > border . width ) / 2 ;
break ;
}
tpg - > border . height = ( ( 9 * w ) / 14 ) & ~ 1 ;
tpg - > border . top = ( h - tpg - > border . height ) / 2 ;
break ;
case TPG_VIDEO_ASPECT_16X9_CENTRE :
if ( tpg - > pix_aspect ) {
tpg - > border . height = tpg - > pix_aspect = = TPG_PIXEL_ASPECT_NTSC ? 368 : 442 ;
tpg - > border . top = ( h - tpg - > border . height ) / 2 ;
break ;
}
if ( 9 * w > = 16 * h ) {
tpg - > border . width = ( ( 16 * h ) / 9 ) & ~ 1 ;
if ( ( ( w - tpg - > border . width ) / 2 ) & ~ 1 )
tpg - > border . width - = 2 ;
tpg - > border . left = ( w - tpg - > border . width ) / 2 ;
break ;
}
tpg - > border . height = ( ( 9 * w ) / 16 ) & ~ 1 ;
tpg - > border . top = ( h - tpg - > border . height ) / 2 ;
break ;
default :
break ;
}
}
static void tpg_precalculate_line ( struct tpg_data * tpg )
{
enum tpg_color contrast ;
unsigned pat ;
unsigned p ;
unsigned x ;
switch ( tpg - > pattern ) {
case TPG_PAT_GREEN :
contrast = TPG_COLOR_100_RED ;
break ;
case TPG_PAT_CSC_COLORBAR :
contrast = TPG_COLOR_CSC_GREEN ;
break ;
default :
contrast = TPG_COLOR_100_GREEN ;
break ;
}
for ( pat = 0 ; pat < tpg_get_pat_lines ( tpg ) ; pat + + ) {
/* Coarse scaling with Bresenham */
unsigned int_part = tpg - > src_width / tpg - > scaled_width ;
unsigned fract_part = tpg - > src_width % tpg - > scaled_width ;
unsigned src_x = 0 ;
unsigned error = 0 ;
for ( x = 0 ; x < tpg - > scaled_width * 2 ; x + = 2 ) {
unsigned real_x = src_x ;
enum tpg_color color1 , color2 ;
u8 pix [ TPG_MAX_PLANES ] [ 8 ] ;
real_x = tpg - > hflip ? tpg - > src_width * 2 - real_x - 2 : real_x ;
color1 = tpg_get_color ( tpg , pat , real_x ) ;
src_x + = int_part ;
error + = fract_part ;
if ( error > = tpg - > scaled_width ) {
error - = tpg - > scaled_width ;
src_x + + ;
}
real_x = src_x ;
real_x = tpg - > hflip ? tpg - > src_width * 2 - real_x - 2 : real_x ;
color2 = tpg_get_color ( tpg , pat , real_x ) ;
src_x + = int_part ;
error + = fract_part ;
if ( error > = tpg - > scaled_width ) {
error - = tpg - > scaled_width ;
src_x + + ;
}
gen_twopix ( tpg , pix , tpg - > hflip ? color2 : color1 , 0 ) ;
gen_twopix ( tpg , pix , tpg - > hflip ? color1 : color2 , 1 ) ;
for ( p = 0 ; p < tpg - > planes ; p + + ) {
unsigned twopixsize = tpg - > twopixelsize [ p ] ;
u8 * pos = tpg - > lines [ pat ] [ p ] + x * twopixsize / 2 ;
memcpy ( pos , pix [ p ] , twopixsize ) ;
}
}
}
for ( x = 0 ; x < tpg - > scaled_width ; x + = 2 ) {
u8 pix [ TPG_MAX_PLANES ] [ 8 ] ;
gen_twopix ( tpg , pix , contrast , 0 ) ;
gen_twopix ( tpg , pix , contrast , 1 ) ;
for ( p = 0 ; p < tpg - > planes ; p + + ) {
unsigned twopixsize = tpg - > twopixelsize [ p ] ;
u8 * pos = tpg - > contrast_line [ p ] + x * twopixsize / 2 ;
memcpy ( pos , pix [ p ] , twopixsize ) ;
}
}
for ( x = 0 ; x < tpg - > scaled_width ; x + = 2 ) {
u8 pix [ TPG_MAX_PLANES ] [ 8 ] ;
gen_twopix ( tpg , pix , TPG_COLOR_100_BLACK , 0 ) ;
gen_twopix ( tpg , pix , TPG_COLOR_100_BLACK , 1 ) ;
for ( p = 0 ; p < tpg - > planes ; p + + ) {
unsigned twopixsize = tpg - > twopixelsize [ p ] ;
u8 * pos = tpg - > black_line [ p ] + x * twopixsize / 2 ;
memcpy ( pos , pix [ p ] , twopixsize ) ;
}
}
for ( x = 0 ; x < tpg - > scaled_width * 2 ; x + = 2 ) {
u8 pix [ TPG_MAX_PLANES ] [ 8 ] ;
gen_twopix ( tpg , pix , TPG_COLOR_RANDOM , 0 ) ;
gen_twopix ( tpg , pix , TPG_COLOR_RANDOM , 1 ) ;
for ( p = 0 ; p < tpg - > planes ; p + + ) {
unsigned twopixsize = tpg - > twopixelsize [ p ] ;
u8 * pos = tpg - > random_line [ p ] + x * twopixsize / 2 ;
memcpy ( pos , pix [ p ] , twopixsize ) ;
}
}
gen_twopix ( tpg , tpg - > textbg , TPG_COLOR_TEXTBG , 0 ) ;
gen_twopix ( tpg , tpg - > textbg , TPG_COLOR_TEXTBG , 1 ) ;
gen_twopix ( tpg , tpg - > textfg , TPG_COLOR_TEXTFG , 0 ) ;
gen_twopix ( tpg , tpg - > textfg , TPG_COLOR_TEXTFG , 1 ) ;
}
/* need this to do rgb24 rendering */
typedef struct { u16 __ ; u8 _ ; } __packed x24 ;
void tpg_gen_text ( struct tpg_data * tpg , u8 * basep [ TPG_MAX_PLANES ] [ 2 ] ,
int y , int x , char * text )
{
int line ;
unsigned step = V4L2_FIELD_HAS_T_OR_B ( tpg - > field ) ? 2 : 1 ;
unsigned div = step ;
unsigned first = 0 ;
unsigned len = strlen ( text ) ;
unsigned p ;
if ( font8x16 = = NULL | | basep = = NULL )
return ;
/* Checks if it is possible to show string */
if ( y + 16 > = tpg - > compose . height | | x + 8 > = tpg - > compose . width )
return ;
if ( len > ( tpg - > compose . width - x ) / 8 )
len = ( tpg - > compose . width - x ) / 8 ;
if ( tpg - > vflip )
y = tpg - > compose . height - y - 16 ;
if ( tpg - > hflip )
x = tpg - > compose . width - x - 8 ;
y + = tpg - > compose . top ;
x + = tpg - > compose . left ;
if ( tpg - > field = = V4L2_FIELD_BOTTOM )
first = 1 ;
else if ( tpg - > field = = V4L2_FIELD_SEQ_TB | | tpg - > field = = V4L2_FIELD_SEQ_BT )
div = 2 ;
for ( p = 0 ; p < tpg - > planes ; p + + ) {
/* Print stream time */
# define PRINTSTR(PIXTYPE) do { \
PIXTYPE fg ; \
PIXTYPE bg ; \
memcpy ( & fg , tpg - > textfg [ p ] , sizeof ( PIXTYPE ) ) ; \
memcpy ( & bg , tpg - > textbg [ p ] , sizeof ( PIXTYPE ) ) ; \
\
for ( line = first ; line < 16 ; line + = step ) { \
int l = tpg - > vflip ? 15 - line : line ; \
PIXTYPE * pos = ( PIXTYPE * ) ( basep [ p ] [ line & 1 ] + \
( ( y * step + l ) / div ) * tpg - > bytesperline [ p ] + \
x * sizeof ( PIXTYPE ) ) ; \
unsigned s ; \
\
for ( s = 0 ; s < len ; s + + ) { \
u8 chr = font8x16 [ text [ s ] * 16 + line ] ; \
\
if ( tpg - > hflip ) { \
pos [ 7 ] = ( chr & ( 0x01 < < 7 ) ? fg : bg ) ; \
pos [ 6 ] = ( chr & ( 0x01 < < 6 ) ? fg : bg ) ; \
pos [ 5 ] = ( chr & ( 0x01 < < 5 ) ? fg : bg ) ; \
pos [ 4 ] = ( chr & ( 0x01 < < 4 ) ? fg : bg ) ; \
pos [ 3 ] = ( chr & ( 0x01 < < 3 ) ? fg : bg ) ; \
pos [ 2 ] = ( chr & ( 0x01 < < 2 ) ? fg : bg ) ; \
pos [ 1 ] = ( chr & ( 0x01 < < 1 ) ? fg : bg ) ; \
pos [ 0 ] = ( chr & ( 0x01 < < 0 ) ? fg : bg ) ; \
} else { \
pos [ 0 ] = ( chr & ( 0x01 < < 7 ) ? fg : bg ) ; \
pos [ 1 ] = ( chr & ( 0x01 < < 6 ) ? fg : bg ) ; \
pos [ 2 ] = ( chr & ( 0x01 < < 5 ) ? fg : bg ) ; \
pos [ 3 ] = ( chr & ( 0x01 < < 4 ) ? fg : bg ) ; \
pos [ 4 ] = ( chr & ( 0x01 < < 3 ) ? fg : bg ) ; \
pos [ 5 ] = ( chr & ( 0x01 < < 2 ) ? fg : bg ) ; \
pos [ 6 ] = ( chr & ( 0x01 < < 1 ) ? fg : bg ) ; \
pos [ 7 ] = ( chr & ( 0x01 < < 0 ) ? fg : bg ) ; \
} \
\
pos + = tpg - > hflip ? - 8 : 8 ; \
} \
} \
} while ( 0 )
switch ( tpg - > twopixelsize [ p ] ) {
case 2 :
PRINTSTR ( u8 ) ; break ;
case 4 :
PRINTSTR ( u16 ) ; break ;
case 6 :
PRINTSTR ( x24 ) ; break ;
case 8 :
PRINTSTR ( u32 ) ; break ;
}
}
}
void tpg_update_mv_step ( struct tpg_data * tpg )
{
int factor = tpg - > mv_hor_mode > TPG_MOVE_NONE ? - 1 : 1 ;
if ( tpg - > hflip )
factor = - factor ;
switch ( tpg - > mv_hor_mode ) {
case TPG_MOVE_NEG_FAST :
case TPG_MOVE_POS_FAST :
tpg - > mv_hor_step = ( ( tpg - > src_width + 319 ) / 320 ) * 4 ;
break ;
case TPG_MOVE_NEG :
case TPG_MOVE_POS :
tpg - > mv_hor_step = ( ( tpg - > src_width + 639 ) / 640 ) * 4 ;
break ;
case TPG_MOVE_NEG_SLOW :
case TPG_MOVE_POS_SLOW :
tpg - > mv_hor_step = 2 ;
break ;
case TPG_MOVE_NONE :
tpg - > mv_hor_step = 0 ;
break ;
}
if ( factor < 0 )
tpg - > mv_hor_step = tpg - > src_width - tpg - > mv_hor_step ;
factor = tpg - > mv_vert_mode > TPG_MOVE_NONE ? - 1 : 1 ;
switch ( tpg - > mv_vert_mode ) {
case TPG_MOVE_NEG_FAST :
case TPG_MOVE_POS_FAST :
tpg - > mv_vert_step = ( ( tpg - > src_width + 319 ) / 320 ) * 4 ;
break ;
case TPG_MOVE_NEG :
case TPG_MOVE_POS :
tpg - > mv_vert_step = ( ( tpg - > src_width + 639 ) / 640 ) * 4 ;
break ;
case TPG_MOVE_NEG_SLOW :
case TPG_MOVE_POS_SLOW :
tpg - > mv_vert_step = 1 ;
break ;
case TPG_MOVE_NONE :
tpg - > mv_vert_step = 0 ;
break ;
}
if ( factor < 0 )
tpg - > mv_vert_step = tpg - > src_height - tpg - > mv_vert_step ;
}
/* Map the line number relative to the crop rectangle to a frame line number */
static unsigned tpg_calc_frameline ( struct tpg_data * tpg , unsigned src_y ,
unsigned field )
{
switch ( field ) {
case V4L2_FIELD_TOP :
return tpg - > crop . top + src_y * 2 ;
case V4L2_FIELD_BOTTOM :
return tpg - > crop . top + src_y * 2 + 1 ;
default :
return src_y + tpg - > crop . top ;
}
}
/*
* Map the line number relative to the compose rectangle to a destination
* buffer line number .
*/
static unsigned tpg_calc_buffer_line ( struct tpg_data * tpg , unsigned y ,
unsigned field )
{
y + = tpg - > compose . top ;
switch ( field ) {
case V4L2_FIELD_SEQ_TB :
if ( y & 1 )
return tpg - > buf_height / 2 + y / 2 ;
return y / 2 ;
case V4L2_FIELD_SEQ_BT :
if ( y & 1 )
return y / 2 ;
return tpg - > buf_height / 2 + y / 2 ;
default :
return y ;
}
}
static void tpg_recalc ( struct tpg_data * tpg )
{
if ( tpg - > recalc_colors ) {
tpg - > recalc_colors = false ;
tpg - > recalc_lines = true ;
tpg_precalculate_colors ( tpg ) ;
}
if ( tpg - > recalc_square_border ) {
tpg - > recalc_square_border = false ;
tpg_calculate_square_border ( tpg ) ;
}
if ( tpg - > recalc_lines ) {
tpg - > recalc_lines = false ;
tpg_precalculate_line ( tpg ) ;
}
}
void tpg_calc_text_basep ( struct tpg_data * tpg ,
u8 * basep [ TPG_MAX_PLANES ] [ 2 ] , unsigned p , u8 * vbuf )
{
unsigned stride = tpg - > bytesperline [ p ] ;
tpg_recalc ( tpg ) ;
basep [ p ] [ 0 ] = vbuf ;
basep [ p ] [ 1 ] = vbuf ;
if ( tpg - > field = = V4L2_FIELD_SEQ_TB )
basep [ p ] [ 1 ] + = tpg - > buf_height * stride / 2 ;
else if ( tpg - > field = = V4L2_FIELD_SEQ_BT )
basep [ p ] [ 0 ] + = tpg - > buf_height * stride / 2 ;
}
void tpg_fillbuffer ( struct tpg_data * tpg , v4l2_std_id std , unsigned p , u8 * vbuf )
{
bool is_tv = std ;
bool is_60hz = is_tv & & ( std & V4L2_STD_525_60 ) ;
unsigned mv_hor_old = tpg - > mv_hor_count % tpg - > src_width ;
unsigned mv_hor_new = ( tpg - > mv_hor_count + tpg - > mv_hor_step ) % tpg - > src_width ;
unsigned mv_vert_old = tpg - > mv_vert_count % tpg - > src_height ;
unsigned mv_vert_new = ( tpg - > mv_vert_count + tpg - > mv_vert_step ) % tpg - > src_height ;
unsigned wss_width ;
unsigned f ;
int hmax = ( tpg - > compose . height * tpg - > perc_fill ) / 100 ;
int h ;
unsigned twopixsize = tpg - > twopixelsize [ p ] ;
unsigned img_width = tpg - > compose . width * twopixsize / 2 ;
unsigned line_offset ;
unsigned left_pillar_width = 0 ;
unsigned right_pillar_start = img_width ;
unsigned stride = tpg - > bytesperline [ p ] ;
unsigned factor = V4L2_FIELD_HAS_T_OR_B ( tpg - > field ) ? 2 : 1 ;
u8 * orig_vbuf = vbuf ;
/* Coarse scaling with Bresenham */
unsigned int_part = ( tpg - > crop . height / factor ) / tpg - > compose . height ;
unsigned fract_part = ( tpg - > crop . height / factor ) % tpg - > compose . height ;
unsigned src_y = 0 ;
unsigned error = 0 ;
tpg_recalc ( tpg ) ;
mv_hor_old = ( mv_hor_old * tpg - > scaled_width / tpg - > src_width ) & ~ 1 ;
mv_hor_new = ( mv_hor_new * tpg - > scaled_width / tpg - > src_width ) & ~ 1 ;
wss_width = tpg - > crop . left < tpg - > src_width / 2 ?
tpg - > src_width / 2 - tpg - > crop . left : 0 ;
if ( wss_width > tpg - > crop . width )
wss_width = tpg - > crop . width ;
wss_width = wss_width * tpg - > scaled_width / tpg - > src_width ;
vbuf + = tpg - > compose . left * twopixsize / 2 ;
line_offset = tpg - > crop . left * tpg - > scaled_width / tpg - > src_width ;
line_offset = ( line_offset & ~ 1 ) * twopixsize / 2 ;
if ( tpg - > crop . left < tpg - > border . left ) {
left_pillar_width = tpg - > border . left - tpg - > crop . left ;
if ( left_pillar_width > tpg - > crop . width )
left_pillar_width = tpg - > crop . width ;
left_pillar_width = ( left_pillar_width * tpg - > scaled_width ) / tpg - > src_width ;
left_pillar_width = ( left_pillar_width & ~ 1 ) * twopixsize / 2 ;
}
if ( tpg - > crop . left + tpg - > crop . width > tpg - > border . left + tpg - > border . width ) {
right_pillar_start = tpg - > border . left + tpg - > border . width - tpg - > crop . left ;
right_pillar_start = ( right_pillar_start * tpg - > scaled_width ) / tpg - > src_width ;
right_pillar_start = ( right_pillar_start & ~ 1 ) * twopixsize / 2 ;
if ( right_pillar_start > img_width )
right_pillar_start = img_width ;
}
f = tpg - > field = = ( is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM ) ;
for ( h = 0 ; h < tpg - > compose . height ; h + + ) {
bool even ;
bool fill_blank = false ;
unsigned frame_line ;
unsigned buf_line ;
unsigned pat_line_old ;
unsigned pat_line_new ;
u8 * linestart_older ;
u8 * linestart_newer ;
u8 * linestart_top ;
u8 * linestart_bottom ;
frame_line = tpg_calc_frameline ( tpg , src_y , tpg - > field ) ;
even = ! ( frame_line & 1 ) ;
buf_line = tpg_calc_buffer_line ( tpg , h , tpg - > field ) ;
src_y + = int_part ;
error + = fract_part ;
if ( error > = tpg - > compose . height ) {
error - = tpg - > compose . height ;
src_y + + ;
}
if ( h > = hmax ) {
if ( hmax = = tpg - > compose . height )
continue ;
if ( ! tpg - > perc_fill_blank )
continue ;
fill_blank = true ;
}
if ( tpg - > vflip )
frame_line = tpg - > src_height - frame_line - 1 ;
if ( fill_blank ) {
linestart_older = tpg - > contrast_line [ p ] ;
linestart_newer = tpg - > contrast_line [ p ] ;
} else if ( tpg - > qual ! = TPG_QUAL_NOISE & &
( frame_line < tpg - > border . top | |
frame_line > = tpg - > border . top + tpg - > border . height ) ) {
linestart_older = tpg - > black_line [ p ] ;
linestart_newer = tpg - > black_line [ p ] ;
} else if ( tpg - > pattern = = TPG_PAT_NOISE | | tpg - > qual = = TPG_QUAL_NOISE ) {
linestart_older = tpg - > random_line [ p ] +
twopixsize * prandom_u32_max ( tpg - > src_width / 2 ) ;
linestart_newer = tpg - > random_line [ p ] +
twopixsize * prandom_u32_max ( tpg - > src_width / 2 ) ;
} else {
pat_line_old = tpg_get_pat_line ( tpg ,
( frame_line + mv_vert_old ) % tpg - > src_height ) ;
pat_line_new = tpg_get_pat_line ( tpg ,
( frame_line + mv_vert_new ) % tpg - > src_height ) ;
linestart_older = tpg - > lines [ pat_line_old ] [ p ] +
mv_hor_old * twopixsize / 2 ;
linestart_newer = tpg - > lines [ pat_line_new ] [ p ] +
mv_hor_new * twopixsize / 2 ;
linestart_older + = line_offset ;
linestart_newer + = line_offset ;
}
if ( is_60hz ) {
linestart_top = linestart_newer ;
linestart_bottom = linestart_older ;
} else {
linestart_top = linestart_older ;
linestart_bottom = linestart_newer ;
}
switch ( tpg - > field ) {
case V4L2_FIELD_INTERLACED :
case V4L2_FIELD_INTERLACED_TB :
case V4L2_FIELD_SEQ_TB :
case V4L2_FIELD_SEQ_BT :
if ( even )
memcpy ( vbuf + buf_line * stride , linestart_top , img_width ) ;
else
memcpy ( vbuf + buf_line * stride , linestart_bottom , img_width ) ;
break ;
case V4L2_FIELD_INTERLACED_BT :
if ( even )
memcpy ( vbuf + buf_line * stride , linestart_bottom , img_width ) ;
else
memcpy ( vbuf + buf_line * stride , linestart_top , img_width ) ;
break ;
case V4L2_FIELD_TOP :
memcpy ( vbuf + buf_line * stride , linestart_top , img_width ) ;
break ;
case V4L2_FIELD_BOTTOM :
memcpy ( vbuf + buf_line * stride , linestart_bottom , img_width ) ;
break ;
case V4L2_FIELD_NONE :
default :
memcpy ( vbuf + buf_line * stride , linestart_older , img_width ) ;
break ;
}
if ( is_tv & & ! is_60hz & & frame_line = = 0 & & wss_width ) {
/*
* Replace the first half of the top line of a 50 Hz frame
* with random data to simulate a WSS signal .
*/
u8 * wss = tpg - > random_line [ p ] +
twopixsize * prandom_u32_max ( tpg - > src_width / 2 ) ;
memcpy ( vbuf + buf_line * stride , wss , wss_width * twopixsize / 2 ) ;
}
}
vbuf = orig_vbuf ;
vbuf + = tpg - > compose . left * twopixsize / 2 ;
src_y = 0 ;
error = 0 ;
for ( h = 0 ; h < tpg - > compose . height ; h + + ) {
unsigned frame_line = tpg_calc_frameline ( tpg , src_y , tpg - > field ) ;
unsigned buf_line = tpg_calc_buffer_line ( tpg , h , tpg - > field ) ;
const struct v4l2_rect * sq = & tpg - > square ;
const struct v4l2_rect * b = & tpg - > border ;
const struct v4l2_rect * c = & tpg - > crop ;
src_y + = int_part ;
error + = fract_part ;
if ( error > = tpg - > compose . height ) {
error - = tpg - > compose . height ;
src_y + + ;
}
if ( tpg - > show_border & & frame_line > = b - > top & &
frame_line < b - > top + b - > height ) {
unsigned bottom = b - > top + b - > height - 1 ;
unsigned left = left_pillar_width ;
unsigned right = right_pillar_start ;
if ( frame_line = = b - > top | | frame_line = = b - > top + 1 | |
frame_line = = bottom | | frame_line = = bottom - 1 ) {
memcpy ( vbuf + buf_line * stride + left , tpg - > contrast_line [ p ] ,
right - left ) ;
} else {
if ( b - > left > = c - > left & &
b - > left < c - > left + c - > width )
memcpy ( vbuf + buf_line * stride + left ,
tpg - > contrast_line [ p ] , twopixsize ) ;
if ( b - > left + b - > width > c - > left & &
b - > left + b - > width < = c - > left + c - > width )
memcpy ( vbuf + buf_line * stride + right - twopixsize ,
tpg - > contrast_line [ p ] , twopixsize ) ;
}
}
if ( tpg - > qual ! = TPG_QUAL_NOISE & & frame_line > = b - > top & &
frame_line < b - > top + b - > height ) {
memcpy ( vbuf + buf_line * stride , tpg - > black_line [ p ] , left_pillar_width ) ;
memcpy ( vbuf + buf_line * stride + right_pillar_start , tpg - > black_line [ p ] ,
img_width - right_pillar_start ) ;
}
if ( tpg - > show_square & & frame_line > = sq - > top & &
frame_line < sq - > top + sq - > height & &
sq - > left < c - > left + c - > width & &
sq - > left + sq - > width > = c - > left ) {
unsigned left = sq - > left ;
unsigned width = sq - > width ;
if ( c - > left > left ) {
width - = c - > left - left ;
left = c - > left ;
}
if ( c - > left + c - > width < left + width )
width - = left + width - c - > left - c - > width ;
left - = c - > left ;
left = ( left * tpg - > scaled_width ) / tpg - > src_width ;
left = ( left & ~ 1 ) * twopixsize / 2 ;
width = ( width * tpg - > scaled_width ) / tpg - > src_width ;
width = ( width & ~ 1 ) * twopixsize / 2 ;
memcpy ( vbuf + buf_line * stride + left , tpg - > contrast_line [ p ] , width ) ;
}
if ( tpg - > insert_sav ) {
unsigned offset = ( tpg - > compose . width / 6 ) * twopixsize ;
u8 * p = vbuf + buf_line * stride + offset ;
unsigned vact = 0 , hact = 0 ;
p [ 0 ] = 0xff ;
p [ 1 ] = 0 ;
p [ 2 ] = 0 ;
p [ 3 ] = 0x80 | ( f < < 6 ) | ( vact < < 5 ) | ( hact < < 4 ) |
( ( hact ^ vact ) < < 3 ) |
( ( hact ^ f ) < < 2 ) |
( ( f ^ vact ) < < 1 ) |
( hact ^ vact ^ f ) ;
}
if ( tpg - > insert_eav ) {
unsigned offset = ( tpg - > compose . width / 6 ) * 2 * twopixsize ;
u8 * p = vbuf + buf_line * stride + offset ;
unsigned vact = 0 , hact = 1 ;
p [ 0 ] = 0xff ;
p [ 1 ] = 0 ;
p [ 2 ] = 0 ;
p [ 3 ] = 0x80 | ( f < < 6 ) | ( vact < < 5 ) | ( hact < < 4 ) |
( ( hact ^ vact ) < < 3 ) |
( ( hact ^ f ) < < 2 ) |
( ( f ^ vact ) < < 1 ) |
( hact ^ vact ^ f ) ;
}
}
}