Commit d9c9414e authored by Vincent Seguin's avatar Vincent Seguin

Suppression de la ligne verte.

Scaling encore optimis�.
Suppression d'un bug causant un segfault sur certaines tailles de fenetre.
parent 558e35cc
...@@ -44,16 +44,11 @@ typedef struct vout_yuv_s ...@@ -44,16 +44,11 @@ typedef struct vout_yuv_s
void * p_base; /* base for all convertion tables */ void * p_base; /* base for all convertion tables */
union union
{ {
struct { u16 *p_red, *p_green, *p_blue; } rgb16; /* color 15, 16 bpp */ u16 * p_gray16; /* gray 16 bits table */
struct { u32 *p_red, *p_green, *p_blue; } rgb32; /* color 24, 32 bpp */ u32 * p_gray32; /* gray 32 bits table */
struct { u16 *p_gray; } gray16; /* gray 15, 16 bpp */ u16 * p_rgb16; /* RGB 16 bits table */
struct { u32 *p_gray; } gray32; /* gray 24, 32 bpp */ u32 * p_rgb32; /* RGB 32 bits table */
} yuv; } yuv;
union
{
u16 * p_rgb16;
u32 * p_rgb32;
} yuv2;//??
/* Temporary convertion buffer and offset array */ /* Temporary convertion buffer and offset array */
void * p_buffer; /* convertion buffer */ void * p_buffer; /* convertion buffer */
......
...@@ -962,7 +962,8 @@ static void RunThread( vout_thread_t *p_vout) ...@@ -962,7 +962,8 @@ static void RunThread( vout_thread_t *p_vout)
* then swap buffers */ * then swap buffers */
vlc_mutex_lock( &p_vout->change_lock ); vlc_mutex_lock( &p_vout->change_lock );
#ifdef DEBUG_VIDEO #ifdef DEBUG_VIDEO
intf_DbgMsg( "picture %p, subpicture %p\n", p_pic, p_subpic ); intf_DbgMsg( "picture %p, subpicture %p in buffer %d, display=%d\n", p_pic, p_subpic,
p_vout->i_buffer_index, b_display && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) );
#endif #endif
if( b_display && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) ) if( b_display && !(p_vout->i_changes & VOUT_NODISPLAY_CHANGE) )
{ {
...@@ -1255,7 +1256,8 @@ static void SetBufferPicture( vout_thread_t *p_vout, picture_t *p_pic ) ...@@ -1255,7 +1256,8 @@ static void SetBufferPicture( vout_thread_t *p_vout, picture_t *p_pic )
int i_area; /* area index */ int i_area; /* area index */
int i_data_index; /* area data index */ int i_data_index; /* area data index */
int i_data_size; /* area data size, in 256 bytes blocs */ int i_data_size; /* area data size, in 256 bytes blocs */
u64 * p_data; /* area data */ u64 * p_data; /* area data, for clearing */
byte_t * p_data8; /* area data, for clearing (slow) */
/* Choose buffer and set display dimensions */ /* Choose buffer and set display dimensions */
p_buffer = &p_vout->p_buffer[ p_vout->i_buffer_index ]; p_buffer = &p_vout->p_buffer[ p_vout->i_buffer_index ];
...@@ -1357,13 +1359,12 @@ static void SetBufferPicture( vout_thread_t *p_vout, picture_t *p_pic ) ...@@ -1357,13 +1359,12 @@ static void SetBufferPicture( vout_thread_t *p_vout, picture_t *p_pic )
for( i_area = 0; i_area < p_buffer->i_areas; i_area++ ) for( i_area = 0; i_area < p_buffer->i_areas; i_area++ )
{ {
#ifdef DEBUG_VIDEO #ifdef DEBUG_VIDEO
intf_DbgMsg("clearing picture %p area: %d-%d\n", p_pic, intf_DbgMsg("clearing picture %p area in buffer %d: %d-%d\n", p_pic,
p_buffer->pi_area_begin[i_area], p_buffer->pi_area_end[i_area]); p_vout->i_buffer_index, p_buffer->pi_area_begin[i_area], p_buffer->pi_area_end[i_area] );
#endif #endif
i_data_size = (p_buffer->pi_area_end[i_area] - p_buffer->pi_area_begin[i_area] + 1) * p_vout->i_bytes_per_line;
p_data = (u64*) (p_buffer->p_data + p_vout->i_bytes_per_line * p_buffer->pi_area_begin[i_area]); p_data = (u64*) (p_buffer->p_data + p_vout->i_bytes_per_line * p_buffer->pi_area_begin[i_area]);
i_data_size = (p_buffer->pi_area_end[i_area] - p_buffer->pi_area_begin[i_area] + 1) * for( i_data_index = i_data_size / 256; i_data_index-- ; )
p_vout->i_bytes_per_line / 256;
for( i_data_index = 0; i_data_index < i_data_size; i_data_index++ )
{ {
/* Clear 256 bytes block */ /* Clear 256 bytes block */
*p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0;
...@@ -1375,12 +1376,16 @@ static void SetBufferPicture( vout_thread_t *p_vout, picture_t *p_pic ) ...@@ -1375,12 +1376,16 @@ static void SetBufferPicture( vout_thread_t *p_vout, picture_t *p_pic )
*p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0;
*p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0; *p_data++ = 0;
} }
i_data_size = (p_buffer->pi_area_end[i_area] - p_buffer->pi_area_begin[i_area] + 1) * for( i_data_index = (i_data_size % 256) / 16; i_data_index--; )
p_vout->i_bytes_per_line % 256 / 4;
for( i_data_index = 0; i_data_index < i_data_size; i_data_index++ )
{ {
/* Clear remaining 4 bytes blocks */ /* Clear remaining 16 bytes blocks */
*p_data++ = 0; *p_data++ = 0; *p_data++ = 0;
}
p_data8 = (byte_t *)p_data;
for( i_data_index = i_data_size % 16; i_data_index--; )
{
/* Clear remaining bytes */
*p_data8++ = 0;
} }
} }
...@@ -1482,8 +1487,9 @@ static void RenderPicture( vout_thread_t *p_vout, picture_t *p_pic ) ...@@ -1482,8 +1487,9 @@ static void RenderPicture( vout_thread_t *p_vout, picture_t *p_pic )
#ifdef DEBUG_VIDEO #ifdef DEBUG_VIDEO
/* Print picture date and rendering time */ /* Print picture date and rendering time */
intf_DbgMsg("picture %p rendered (%ld us), display date: %s\n", p_pic, intf_DbgMsg("picture %p rendered in buffer %d (%ld us), display date: %s\n", p_pic,
(long) (mdate() - render_time), mstrtime( psz_date, p_pic->date )); p_vout->i_buffer_index, (long) (mdate() - render_time),
mstrtime( psz_date, p_pic->date ));
#endif #endif
} }
......
...@@ -41,6 +41,14 @@ const int MATRIX_COEFFICIENTS_TABLE[8][4] = ...@@ -41,6 +41,14 @@ const int MATRIX_COEFFICIENTS_TABLE[8][4] =
{117579, 136230, 16907, 35559} /* SMPTE 240M (1987) */ {117579, 136230, 16907, 35559} /* SMPTE 240M (1987) */
}; };
/* Margins in convertion tables - these margins are used in case a RGB convertion
* would give a value outside the 0-255 range. */
#define RED_MARGIN 178
#define GREEN_MARGIN 135
#define BLUE_MARGIN 224
#define GRAY_MARGIN 384
//??
#define SHIFT 20 #define SHIFT 20
#define U_GREEN_COEF ((int)(-0.391 * (1<<SHIFT) / 1.164)) #define U_GREEN_COEF ((int)(-0.391 * (1<<SHIFT) / 1.164))
#define U_BLUE_COEF ((int)(2.018 * (1<<SHIFT) / 1.164)) #define U_BLUE_COEF ((int)(2.018 * (1<<SHIFT) / 1.164))
...@@ -178,14 +186,14 @@ int vout_InitYUV( vout_thread_t *p_vout ) ...@@ -178,14 +186,14 @@ int vout_InitYUV( vout_thread_t *p_vout )
{ {
case 15: case 15:
case 16: case 16:
tables_size = sizeof( u16 ) * (1024 * (p_vout->b_grayscale ? 1 : 3) + 1935); tables_size = sizeof( u16 ) * (p_vout->b_grayscale ? 1034 : 1935);
break; break;
case 24: case 24:
case 32: case 32:
#ifndef DEBUG #ifndef DEBUG
default: default:
#endif #endif
tables_size = sizeof( u32 ) * (1024 * (p_vout->b_grayscale ? 1 : 3) + 1935); tables_size = sizeof( u32 ) * (p_vout->b_grayscale ? 1024 : 1935);
break; break;
#ifdef DEBUG #ifdef DEBUG
default: default:
...@@ -383,10 +391,10 @@ static void SetYUV( vout_thread_t *p_vout ) ...@@ -383,10 +391,10 @@ static void SetYUV( vout_thread_t *p_vout )
{ {
case 15: case 15:
case 16: case 16:
p_vout->yuv.yuv.gray16.p_gray = (u16 *)p_vout->yuv.p_base + 384; p_vout->yuv.yuv.p_gray16 = (u16 *)p_vout->yuv.p_base + GRAY_MARGIN;
for( i_index = -384; i_index < 640; i_index++) for( i_index = -GRAY_MARGIN; i_index < 256 + GRAY_MARGIN; i_index++)
{ {
p_vout->yuv.yuv.gray16.p_gray[ i_index ] = p_vout->yuv.yuv.p_gray16[ i_index ] =
((pi_gamma[CLIP_BYTE( i_index )] >> i_red_right) << i_red_left) | ((pi_gamma[CLIP_BYTE( i_index )] >> i_red_right) << i_red_left) |
((pi_gamma[CLIP_BYTE( i_index )] >> i_green_right) << i_green_left) | ((pi_gamma[CLIP_BYTE( i_index )] >> i_green_right) << i_green_left) |
((pi_gamma[CLIP_BYTE( i_index )] >> i_blue_right) << i_blue_left); ((pi_gamma[CLIP_BYTE( i_index )] >> i_blue_right) << i_blue_left);
...@@ -394,10 +402,10 @@ static void SetYUV( vout_thread_t *p_vout ) ...@@ -394,10 +402,10 @@ static void SetYUV( vout_thread_t *p_vout )
break; break;
case 24: case 24:
case 32: case 32:
p_vout->yuv.yuv.gray32.p_gray = (u32 *)p_vout->yuv.p_base + 384; p_vout->yuv.yuv.p_gray32 = (u32 *)p_vout->yuv.p_base + GRAY_MARGIN;
for( i_index = -384; i_index < 640; i_index++) for( i_index = -GRAY_MARGIN; i_index < 256 + GRAY_MARGIN; i_index++)
{ {
p_vout->yuv.yuv.gray32.p_gray[ i_index ] = p_vout->yuv.yuv.p_gray32[ i_index ] =
((pi_gamma[CLIP_BYTE( i_index )] >> i_red_right) << i_red_left) | ((pi_gamma[CLIP_BYTE( i_index )] >> i_red_right) << i_red_left) |
((pi_gamma[CLIP_BYTE( i_index )] >> i_green_right) << i_green_left) | ((pi_gamma[CLIP_BYTE( i_index )] >> i_green_right) << i_green_left) |
((pi_gamma[CLIP_BYTE( i_index )] >> i_blue_right) << i_blue_left); ((pi_gamma[CLIP_BYTE( i_index )] >> i_blue_right) << i_blue_left);
...@@ -412,51 +420,53 @@ static void SetYUV( vout_thread_t *p_vout ) ...@@ -412,51 +420,53 @@ static void SetYUV( vout_thread_t *p_vout )
{ {
case 15: case 15:
case 16: case 16:
p_vout->yuv.yuv.rgb16.p_red = (u16 *)p_vout->yuv.p_base + 384; p_vout->yuv.yuv.p_rgb16 = (u16 *)p_vout->yuv.p_base;
p_vout->yuv.yuv.rgb16.p_green = (u16 *)p_vout->yuv.p_base + 1024 + 384; for( i_index = 0; i_index < RED_MARGIN; i_index++ )
p_vout->yuv.yuv.rgb16.p_blue = (u16 *)p_vout->yuv.p_base + 2*1024 + 384;
p_vout->yuv.yuv2.p_rgb16 = (u16 *)p_vout->yuv.p_base + 3*1024;
for( i_index = -384; i_index < 640; i_index++)
{ {
p_vout->yuv.yuv.rgb16.p_red[i_index] = (pi_gamma[CLIP_BYTE(i_index)]>>i_red_right)<<i_red_left; p_vout->yuv.yuv.p_rgb16[1501 - RED_MARGIN + i_index] = (pi_gamma[0]>>i_red_right)<<i_red_left;
p_vout->yuv.yuv.rgb16.p_green[i_index] = (pi_gamma[CLIP_BYTE(i_index)]>>i_green_right)<<i_green_left; p_vout->yuv.yuv.p_rgb16[1501 + 256 + i_index] = (pi_gamma[255]>>i_red_right)<<i_red_left;
p_vout->yuv.yuv.rgb16.p_blue[i_index] = (pi_gamma[CLIP_BYTE(i_index)]>>i_blue_right)<<i_blue_left;
} }
for( i_index = 0; i_index < 178; i_index++ ) for( i_index = 0; i_index < GREEN_MARGIN; i_index++ )
{ {
p_vout->yuv.yuv2.p_rgb16[1501 - 178 + i_index] = (pi_gamma[0]>>i_red_right)<<i_red_left; p_vout->yuv.yuv.p_rgb16[135 - GREEN_MARGIN + i_index] = (pi_gamma[0]>>i_green_right)<<i_green_left;
p_vout->yuv.yuv2.p_rgb16[1501 + 256 + i_index] = (pi_gamma[255]>>i_red_right)<<i_red_left; p_vout->yuv.yuv.p_rgb16[135 + 256 + i_index] = (pi_gamma[255]>>i_green_right)<<i_green_left;
} }
for( i_index = 0; i_index < 135; i_index++ ) for( i_index = 0; i_index < BLUE_MARGIN; i_index++ )
{ {
p_vout->yuv.yuv2.p_rgb16[135 - 135 + i_index] = (pi_gamma[0]>>i_green_right)<<i_green_left; p_vout->yuv.yuv.p_rgb16[818 - BLUE_MARGIN + i_index] = (pi_gamma[0]>>i_blue_right)<<i_blue_left;
p_vout->yuv.yuv2.p_rgb16[135 + 256 + i_index] = (pi_gamma[255]>>i_green_right)<<i_green_left; p_vout->yuv.yuv.p_rgb16[818 + BLUE_MARGIN + i_index] = (pi_gamma[255]>>i_blue_right)<<i_blue_left;
}
for( i_index = 0; i_index < 224; i_index++ )
{
p_vout->yuv.yuv2.p_rgb16[818 - 224 + i_index] = (pi_gamma[0]>>i_blue_right)<<i_blue_left;
p_vout->yuv.yuv2.p_rgb16[818 + 256 + i_index] = (pi_gamma[255]>>i_blue_right)<<i_blue_left;
} }
for( i_index = 0; i_index < 256; i_index++ ) for( i_index = 0; i_index < 256; i_index++ )
{ {
p_vout->yuv.yuv2.p_rgb16[1501 + i_index] = (pi_gamma[i_index]>>i_red_right)<<i_red_left; p_vout->yuv.yuv.p_rgb16[1501 + i_index] = (pi_gamma[i_index]>>i_red_right)<<i_red_left;
p_vout->yuv.yuv2.p_rgb16[135 + i_index] = (pi_gamma[i_index]>>i_green_right)<<i_green_left; p_vout->yuv.yuv.p_rgb16[135 + i_index] = (pi_gamma[i_index]>>i_green_right)<<i_green_left;
p_vout->yuv.yuv2.p_rgb16[818 + i_index] = (pi_gamma[i_index]>>i_blue_right)<<i_blue_left; p_vout->yuv.yuv.p_rgb16[818 + i_index] = (pi_gamma[i_index]>>i_blue_right)<<i_blue_left;
} }
break; break;
case 24: case 24:
case 32: case 32:
p_vout->yuv.yuv.rgb32.p_red = (u32 *)p_vout->yuv.p_base + 384; p_vout->yuv.yuv.p_rgb32 = (u32 *)p_vout->yuv.p_base;
p_vout->yuv.yuv.rgb32.p_green = (u32 *)p_vout->yuv.p_base + 1024 + 384; for( i_index = 0; i_index < RED_MARGIN; i_index++ )
p_vout->yuv.yuv.rgb32.p_blue = (u32 *)p_vout->yuv.p_base + 2*1024 + 384; {
p_vout->yuv.yuv2.p_rgb32 = (u32 *)p_vout->yuv.p_base + 3*1024; p_vout->yuv.yuv.p_rgb32[1501 - RED_MARGIN + i_index] = (pi_gamma[0]>>i_red_right)<<i_red_left;
for( i_index = -384; i_index < 640; i_index++) p_vout->yuv.yuv.p_rgb32[1501 + 256 + i_index] = (pi_gamma[255]>>i_red_right)<<i_red_left;
}
for( i_index = 0; i_index < GREEN_MARGIN; i_index++ )
{
p_vout->yuv.yuv.p_rgb32[135 - GREEN_MARGIN + i_index] = (pi_gamma[0]>>i_green_right)<<i_green_left;
p_vout->yuv.yuv.p_rgb32[135 + 256 + i_index] = (pi_gamma[255]>>i_green_right)<<i_green_left;
}
for( i_index = 0; i_index < BLUE_MARGIN; i_index++ )
{
p_vout->yuv.yuv.p_rgb32[818 - BLUE_MARGIN + i_index] = (pi_gamma[0]>>i_blue_right)<<i_blue_left;
p_vout->yuv.yuv.p_rgb32[818 + BLUE_MARGIN + i_index] = (pi_gamma[255]>>i_blue_right)<<i_blue_left;
}
for( i_index = 0; i_index < 256; i_index++ )
{ {
p_vout->yuv.yuv.rgb32.p_red[i_index] = (pi_gamma[CLIP_BYTE(i_index)]>>i_red_right)<<i_red_left; p_vout->yuv.yuv.p_rgb32[1501 + i_index] = (pi_gamma[i_index]>>i_red_right)<<i_red_left;
p_vout->yuv.yuv.rgb32.p_green[i_index] = (pi_gamma[CLIP_BYTE(i_index)]>>i_green_right)<<i_green_left; p_vout->yuv.yuv.p_rgb32[135 + i_index] = (pi_gamma[i_index]>>i_green_right)<<i_green_left;
p_vout->yuv.yuv.rgb32.p_blue[i_index] = (pi_gamma[CLIP_BYTE(i_index)]>>i_blue_right)<<i_blue_left; p_vout->yuv.yuv.p_rgb32[818 + i_index] = (pi_gamma[i_index]>>i_blue_right)<<i_blue_left;
} }
//?? walken's yuv
break; break;
} }
} }
...@@ -522,7 +532,7 @@ static void ConvertY4Gray16( p_vout_thread_t p_vout, u16 *p_pic, yuv_data_t *p_y ...@@ -522,7 +532,7 @@ static void ConvertY4Gray16( p_vout_thread_t p_vout, u16 *p_pic, yuv_data_t *p_y
u16 * p_gray; /* gray table */ u16 * p_gray; /* gray table */
int i_x, i_y; /* picture coordinates */ int i_x, i_y; /* picture coordinates */
p_gray = p_vout->yuv.yuv.gray16.p_gray; p_gray = p_vout->yuv.yuv.p_gray16;
CONVERT_YUV_GRAY CONVERT_YUV_GRAY
} }
...@@ -546,7 +556,7 @@ static void ConvertY4Gray32( p_vout_thread_t p_vout, u32 *p_pic, yuv_data_t *p_y ...@@ -546,7 +556,7 @@ static void ConvertY4Gray32( p_vout_thread_t p_vout, u32 *p_pic, yuv_data_t *p_y
u32 * p_gray; /* gray table */ u32 * p_gray; /* gray table */
int i_x, i_y; /* picture coordinates */ int i_x, i_y; /* picture coordinates */
p_gray = p_vout->yuv.yuv.gray32.p_gray; p_gray = p_vout->yuv.yuv.p_gray32;
CONVERT_YUV_GRAY CONVERT_YUV_GRAY
} }
...@@ -588,7 +598,7 @@ static void ConvertYUV420RGB16( p_vout_thread_t p_vout, u16 *p_pic, yuv_data_t * ...@@ -588,7 +598,7 @@ static void ConvertYUV420RGB16( p_vout_thread_t p_vout, u16 *p_pic, yuv_data_t *
*/ */
i_pic_line_width -= i_pic_width; i_pic_line_width -= i_pic_width;
i_chroma_width = i_width / 2; i_chroma_width = i_width / 2;
p_yuv = p_vout->yuv.yuv2.p_rgb16; p_yuv = p_vout->yuv.yuv.p_rgb16;
/* /*
* Set scalings * Set scalings
...@@ -709,7 +719,7 @@ static void ConvertYUV420RGB16( p_vout_thread_t p_vout, u16 *p_pic, yuv_data_t * ...@@ -709,7 +719,7 @@ static void ConvertYUV420RGB16( p_vout_thread_t p_vout, u16 *p_pic, yuv_data_t *
} }
/* If line is odd, rewind U and V samples */ /* If line is odd, rewind U and V samples */
if( i_y & 0x1 ) if( !(i_y & 0x1) )
{ {
p_u -= i_chroma_width; p_u -= i_chroma_width;
p_v -= i_chroma_width; p_v -= i_chroma_width;
...@@ -729,7 +739,7 @@ static void ConvertYUV420RGB16( p_vout_thread_t p_vout, u16 *p_pic, yuv_data_t * ...@@ -729,7 +739,7 @@ static void ConvertYUV420RGB16( p_vout_thread_t p_vout, u16 *p_pic, yuv_data_t *
{ {
/* Height reduction: skip next source line */ /* Height reduction: skip next source line */
p_y += i_width; p_y += i_width;
if( ! (++i_y & 0x1) ) if( ++i_y & 0x1 )
{ {
p_u += i_chroma_width; p_u += i_chroma_width;
p_v += i_chroma_width; p_v += i_chroma_width;
...@@ -862,62 +872,6 @@ static void ConvertYUV444RGB32( p_vout_thread_t p_vout, u32 *p_pic, yuv_data_t * ...@@ -862,62 +872,6 @@ static void ConvertYUV444RGB32( p_vout_thread_t p_vout, u32 *p_pic, yuv_data_t *
* calculated to minimize the cache interactions of the 3 tables. * calculated to minimize the cache interactions of the 3 tables.
*/ */
static int rgbTable32 (int table [1935],
int redMask, int greenMask, int blueMask,
unsigned char gamma[256])
{
int redRight;
int redLeft;
int greenRight;
int greenLeft;
int blueRight;
int blueLeft;
int * redTable;
int * greenTable;
int * blueTable;
int i;
int y;
MaskToShift (&redRight, &redLeft, redMask);
MaskToShift (&greenRight, &greenLeft, greenMask);
MaskToShift (&blueRight, &blueLeft, blueMask);
/*
* green blue red +- 2 just to be sure
* green = 0-525 [151-370]
* blue = 594-1297 [834-1053] <834-29>
* red = 1323-1934 [1517-1736] <493-712>
*/
redTable = table + 1501;
greenTable = table + 135;
blueTable = table + 818;
for (i = 0; i < 178; i++) {
redTable[i-178] = 0;
redTable[i+256] = redMask;
}
for (i = 0; i < 135; i++) {
greenTable[i-135] = 0;
greenTable[i+256] = greenMask;
}
for (i = 0; i < 224; i++) {
blueTable[i-224] = 0;
blueTable[i+256] = blueMask;
}
for (i = 0; i < 256; i++) {
y = gamma[i];
redTable[i] = ((y >> redRight) << redLeft);
greenTable[i] = ((y >> greenRight) << greenLeft);
blueTable[i] = ((y >> blueRight) << blueLeft);
}
return 0;
}
static void yuvToRgb24 (unsigned char * Y, static void yuvToRgb24 (unsigned char * Y,
unsigned char * U, unsigned char * V, unsigned char * U, unsigned char * V,
char * dest, int table[1935], int width) char * dest, int table[1935], int width)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment