Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- softcomp - 240p component video signal, in a framebuffer
- connect VGA R/G/B lines to Pr/Y/Pb on a television set and watch it go
- modeline that "works for me": "softcomp" 6.05 378 380 382 384 258 260 262 263
- note that the total H clocks (384) is SIGNAL_WIDTH and the total lines (263) is PICTURE_LINES
- ideas for improvements:
- 1. interlaced support. I had began with this initially but it seemed better to start with a 240p version.
- also, makes KDE start vsyncing to the 30hz updates... annoying as hell.
- 2. composite (CVBS) signal. I had also tried this, but couldn't make my TV recognize the colorburst.
- tried for hours, made nothing happen; likely requires too precise a colorburst frequency to work.
- I don't have a scope so I can't verify what's really wrong with my attempts.
- 3. find a modeline that actually allows us to skip the VGA card's blanking alltogether - free 600MHz RAMDAC!
- 4. scrap the whole thing and do it all with Xorg colorspace tweaks and a clever modeline
- I had a real copyright here, but let's just call me !!3Kn5kfZkmkl
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- video timing/level info from:
- - http://www.batsocks.co.uk/readme/video_timing.htm
- - http://martin.hinner.info/vga/pal.html
- - http://www.ni.com/white-paper/4750/en
- - http://www.maximintegrated.com/app-notes/index.mvp/id/1184
- - http://codeandlife.com/2012/07/31/realtime-composite-video-decoding-with-picoscope/
- */
- #include <stdio.h>
- #include <SDL.h>
- #include <SDL_opengl.h>
- #include <math.h>
- //video signal is typically -40 to 120 IRE
- //this is a range of 160.
- //opengl output isn't in a linear colorspace, but it's "close enough" for this kind of math to work...
- //note that we're 40IRE higher than standards, because we can't go negative.
- #define SYNC_LEVEL (0.0f / 160.0f)
- #define BLANK_LEVEL (40.0f / 160.0f)
- //if you're giving softcomp a 16...235 image, use the lower black level and higher white level.
- //if you're using the full 0...255 range in your image, then it's up to us to make the correction - use tighter range instead.
- //#define BLACK_LEVEL (47.5f / 160.0f)
- #define BLACK_LEVEL (40.0f / 160.0f)
- //#define WHITE_LEVEL (140.0f/ 160.0f)
- #define WHITE_LEVEL (160.0f / 160.0f)
- #define NEUTRAL_LEVEL ((BLACK_LEVEL + WHITE_LEVEL)/2.0f)
- #define PICTURE_LINES 263
- #define SIGNAL_WIDTH 384
- #define ACTIVE_VIDEO_LINES 240
- //our signal geometry is aligned with the assumption that you can't get all PICTURE_LINES lines active out of your VGA card,
- //and that within those lines you can't get all SIGNAL_WIDTH pixels active.
- //get as many lines and pixels/line active as you can, while maintaining a total of PICTURE_LINES video lines and SIGNAL_WIDTH pixel clocks/line.
- //our window goes in the upper-left corner of your display, potentially cutting off the bottom/right edges.
- //the VGA card typically outputs RGB=0v during its sync/blanking period, meaning that any cut-off image will be effectively 0 (sync!)
- //we make do, but this video signal is NOT pretty as a result - probably has a solid sync-level pulse for a few lines instead of real vsyncs
- #define LINE_PERIOD 63.55 //63.4710743801652892561983471074380165289256192 //microseconds
- #define HSYNC_WIDTH (4.7 / LINE_PERIOD) //hsync put at right edge of picture, so VGA card blanking doesn't kill it
- #define H_FRONT_PORCH (1.5 / LINE_PERIOD)
- #define H_BACK_PORCH (4.5 / LINE_PERIOD)
- #define VSYNC_PULSE_WIDTH (27.1 / LINE_PERIOD) //"broad sync section"
- #define VSYNC_PULSE_LINES 3 //"broad sync" / "vertical sync pulses"
- #define VSYNC_LEVEL_LINES 3 //"pre-equalising pulses", "post-equalising pulses"
- #define VSYNC_BLANK_LINES 10
- const char *vertshader =
- "varying vec4 color; \
- void main() \
- { \
- gl_Position = ftransform(); \
- color = gl_Color;\
- gl_TexCoord[0] = gl_MultiTexCoord0;\
- }"
- ;
- const char *fragshader =
- "uniform sampler2D tex; \
- varying vec4 color; \
- mat4 color_matrix = \
- mat4( \
- 0.5, -0.419, 0.081, 0.5, \
- 0.299, 0.587, 0.144, 0.0, \
- -0.169, -0.331, 0.5, 0.5, \
- 0.0, 0.0, 0.0, 1.0 \
- \
- ); \
- \
- void main() \
- { \
- \
- vec4 texturecolor = texture2D(tex,gl_TexCoord[0].xy); \
- gl_FragColor = color*(texturecolor*color_matrix); \
- }"
- ;
- int main(int argc, const char **argv)
- {
- //init SDL and make screen surface in a borderless window
- SDL_Init(SDL_INIT_VIDEO);
- SDL_Surface *screen = SDL_SetVideoMode(SIGNAL_WIDTH, PICTURE_LINES, /*bpp*/0, SDL_NOFRAME | SDL_OPENGL);
- //load test image into opengl
- FILE *imagefile = fopen("lena256.raw", "rb");
- unsigned char *imagedata = (unsigned char*)malloc(196608);
- fread(imagedata, 1, 196608, imagefile);
- GLuint imageTexture;
- glGenTextures(1, &imageTexture);
- glBindTexture(GL_TEXTURE_2D, imageTexture);
- glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, imagedata);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glBindTexture(GL_TEXTURE_2D, 0);
- //load shader into opengl - boilerplate code from NeHe (http://nehe.gamedev.net/article/glsl_an_introduction/25007/)
- //and from lighthouse3d (http://www.lighthouse3d.com/tutorials/glsl-tutorial/troubleshooting-the-infolog/)
- GLenum my_program;
- GLenum my_vertex_shader;
- GLenum my_fragment_shader;
- // Create Shader And Program Objects
- my_program = glCreateProgramObjectARB();
- my_vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
- my_fragment_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
- // Load Shader Sources
- glShaderSourceARB(my_vertex_shader, 1, &vertshader, NULL);
- glShaderSourceARB(my_fragment_shader, 1, &fragshader, NULL);
- // Compile The Shaders
- glCompileShaderARB(my_vertex_shader);
- glCompileShaderARB(my_fragment_shader);
- int infologLength = 0;
- int charsWritten = 0;
- char *infoLog;
- glGetShaderiv(my_vertex_shader, GL_INFO_LOG_LENGTH,&infologLength);
- if (infologLength > 0)
- {
- infoLog = (char *)malloc(infologLength);
- glGetShaderInfoLog(my_vertex_shader, infologLength, &charsWritten, infoLog);
- printf("%s\n",infoLog);
- free(infoLog);
- }
- infologLength = 0;
- charsWritten = 0;
- glGetShaderiv(my_fragment_shader, GL_INFO_LOG_LENGTH,&infologLength);
- if (infologLength > 0)
- {
- infoLog = (char *)malloc(infologLength);
- glGetShaderInfoLog(my_fragment_shader, infologLength, &charsWritten, infoLog);
- printf("%s\n",infoLog);
- free(infoLog);
- }
- // Attach The Shader Objects To The Program Object
- glAttachObjectARB(my_program, my_vertex_shader);
- glAttachObjectARB(my_program, my_fragment_shader);
- // Link The Program Object
- glLinkProgramARB(my_program);
- infologLength = 0;
- charsWritten = 0;
- glGetProgramiv(my_program, GL_INFO_LOG_LENGTH,&infologLength);
- if (infologLength > 0)
- {
- infoLog = (char *)malloc(infologLength);
- glGetProgramInfoLog(my_program, infologLength, &charsWritten, infoLog);
- printf("%s\n",infoLog);
- free(infoLog);
- }
- //set up basic GL properties
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f); //raster coordinates from 0,0 (top-left) to 1,1.
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- //printf("%f\n", (1.0f - (H_BACK_PORCH+H_FRONT_PORCH+HSYNC_WIDTH)) / (COLORBURST_PERIOD));
- //main loop
- int done = 0;
- int framenum = 0;
- while(!done)
- {
- //clear whole signal to blanking level
- glViewport(0,0,SIGNAL_WIDTH,263);
- glClearColor(NEUTRAL_LEVEL,BLANK_LEVEL,NEUTRAL_LEVEL,1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
- //horizontal pulse on right edge of picture until vsync
- glViewport(0,(VSYNC_LEVEL_LINES+VSYNC_PULSE_LINES+VSYNC_LEVEL_LINES),SIGNAL_WIDTH,PICTURE_LINES-(VSYNC_LEVEL_LINES+VSYNC_PULSE_LINES+VSYNC_LEVEL_LINES));
- glColor3f(SYNC_LEVEL, SYNC_LEVEL, SYNC_LEVEL);
- glBegin(GL_QUADS);
- glVertex3f(1.0f, 0.0f, -1.0f);
- glVertex3f(1.0f, 1.0f, -1.0f);
- glVertex3f(1.0f - HSYNC_WIDTH, 1.0f, -1.0f);
- glVertex3f(1.0f - HSYNC_WIDTH, 0.0f, -1.0f);
- glEnd();
- //bring active video area up to black level
- glViewport(0,(PICTURE_LINES-ACTIVE_VIDEO_LINES) - VSYNC_BLANK_LINES, SIGNAL_WIDTH, ACTIVE_VIDEO_LINES);
- glColor3f(BLACK_LEVEL, BLACK_LEVEL, BLACK_LEVEL);
- glBegin(GL_QUADS);
- glVertex3f(H_BACK_PORCH, 0.0f, -1.0f);
- glVertex3f(H_BACK_PORCH, 1.0f, -1.0f);
- glVertex3f(1.0f - (H_FRONT_PORCH+HSYNC_WIDTH), 1.0f, -1.0f);
- glVertex3f(1.0f - (H_FRONT_PORCH+HSYNC_WIDTH), 0.0f, -1.0f);
- glEnd();
- //create RGB->YUV matrix - use A(W) component (always 1 in source image) to bias U and V
- /*float color_matrix[16] =
- {
- 0.299, 0.587, 0.144, 0.0,
- -0.169, -0.331, 0.5, 0.5,
- 0.5, -0.419, 0.081, 0.5,
- 0.0, 0.0, 0.0, 1.0
- };*/
- /*float color_matrix[16] =
- {
- 0.299, -0.169, 0.5, 0.0,
- 0.587, -0.331, -0.419, 0.0,
- 0.144, 0.5, 0.081, 0.0,
- 0.0, 0.5, 0.5, 1.0
- };*/
- //glMatrixMode(GL_COLOR);
- //glLoadTransposeMatrixf(color_matrix);
- //it couldn't have been that easy, huh?
- //use shader.
- glUseProgramObjectARB(my_program);
- //whole picture is at black level - bring potentially up to white level with additive blending
- //blend picture in using YUV color transform
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE);
- glEnable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, imageTexture);
- glColor3f( WHITE_LEVEL-BLANK_LEVEL, WHITE_LEVEL-BLANK_LEVEL, WHITE_LEVEL-BLANK_LEVEL);
- glBegin(GL_QUADS);
- glTexCoord2f(0.0f,0.0f);
- glVertex3f(H_BACK_PORCH, 0.0f, -1.0f);
- glTexCoord2f(0.0f,1.0f);
- glVertex3f(H_BACK_PORCH, 1.0f, -1.0f);
- glTexCoord2f(1.0f,1.0f);
- glVertex3f(1.0f - (H_FRONT_PORCH+HSYNC_WIDTH), 1.0f, -1.0f);
- glTexCoord2f(1.0f,0.0f);
- glVertex3f(1.0f - (H_FRONT_PORCH+HSYNC_WIDTH), 0.0f, -1.0f);
- glEnd();
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_BLEND);
- //glLoadIdentity();
- glUseProgramObjectARB(0);
- //vsync goes at bottom of picture, because it will likely be clobbered by your video card blanking
- //pre-equalising pulses
- int i;
- for(i=0;i<VSYNC_LEVEL_LINES;i++)
- {
- glViewport(0,i+(VSYNC_LEVEL_LINES+VSYNC_PULSE_LINES),SIGNAL_WIDTH,1);
- glColor3f(SYNC_LEVEL, SYNC_LEVEL, SYNC_LEVEL);
- glBegin(GL_QUADS);
- glVertex3f(1.0f, 0.0f, -1.0f);
- glVertex3f(1.0f, 1.0f, -1.0f);
- glVertex3f(1.0f-HSYNC_WIDTH, 1.0f, -1.0f);
- glVertex3f(1.0f-HSYNC_WIDTH, 0.0f, -1.0f);
- glVertex3f(0.5f, 0.0f, -1.0f);
- glVertex3f(0.5f, 1.0f, -1.0f);
- glVertex3f(0.5f - HSYNC_WIDTH, 1.0f, -1.0f);
- glVertex3f(0.5f - HSYNC_WIDTH, 0.0f, -1.0f);
- glEnd();
- }
- //sync pulses
- for(i=0;i<VSYNC_PULSE_LINES;i++)
- {
- glViewport(0,i+(VSYNC_LEVEL_LINES),SIGNAL_WIDTH,1);
- glColor3f(SYNC_LEVEL, SYNC_LEVEL, SYNC_LEVEL);
- glBegin(GL_QUADS);
- glVertex3f(1.0f, 0.0f, -1.0f);
- glVertex3f(1.0f, 1.0f, -1.0f);
- glVertex3f(1.0f-VSYNC_PULSE_WIDTH, 1.0f, -1.0f);
- glVertex3f(1.0f-VSYNC_PULSE_WIDTH, 0.0f, -1.0f);
- glVertex3f(0.5f, 0.0f, -1.0f);
- glVertex3f(0.5f, 1.0f, -1.0f);
- glVertex3f(0.5f - VSYNC_PULSE_WIDTH, 1.0f, -1.0f);
- glVertex3f(0.5f - VSYNC_PULSE_WIDTH, 0.0f, -1.0f);
- glEnd();
- }
- //post-equalizing pulses
- for(i=0;i<VSYNC_LEVEL_LINES;i++)
- {
- glViewport(0,i,SIGNAL_WIDTH,1);
- glColor3f(SYNC_LEVEL, SYNC_LEVEL, SYNC_LEVEL);
- glBegin(GL_QUADS);
- glVertex3f(1.0f, 0.0f, -1.0f);
- glVertex3f(1.0f, 1.0f, -1.0f);
- glVertex3f(1.0f - HSYNC_WIDTH, 1.0f, -1.0f);
- glVertex3f(1.0f - HSYNC_WIDTH, 0.0f, -1.0f);
- glVertex3f(0.5f, 0.0f, -1.0f);
- glVertex3f(0.5f, 1.0f, -1.0f);
- glVertex3f(0.5f - HSYNC_WIDTH, 1.0f, -1.0f);
- glVertex3f(0.5f - HSYNC_WIDTH, 0.0f, -1.0f);
- glEnd();
- }
- //page-flip, displaying the new image
- SDL_GL_SwapBuffers();
- //poll input, break main loop if there's a quit event
- SDL_Event event;
- while(SDL_PollEvent(&event))
- {
- if(event.type == SDL_QUIT)
- done = 1;
- }
- framenum++;
- }
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement