Advertisement
Guest User

Untitled

a guest
Oct 4th, 2014
265
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 12.76 KB | None | 0 0
  1. /*
  2. softcomp - 240p component video signal, in a framebuffer
  3. connect VGA R/G/B lines to Pr/Y/Pb on a television set and watch it go
  4. modeline that "works for me": "softcomp" 6.05 378 380 382 384 258 260 262 263
  5. note that the total H clocks (384) is SIGNAL_WIDTH and the total lines (263) is PICTURE_LINES
  6.  
  7.  
  8. ideas for improvements:
  9. 1. interlaced support. I had began with this initially but it seemed better to start with a 240p version.
  10.    also, makes KDE start vsyncing to the 30hz updates... annoying as hell.
  11. 2. composite (CVBS) signal. I had also tried this, but couldn't make my TV recognize the colorburst.
  12.    tried for hours, made nothing happen; likely requires too precise a colorburst frequency to work.
  13.    I don't have a scope so I can't verify what's really wrong with my attempts.
  14. 3. find a modeline that actually allows us to skip the VGA card's blanking alltogether - free 600MHz RAMDAC!
  15. 4. scrap the whole thing and do it all with Xorg colorspace tweaks and a clever modeline
  16.  
  17. I had a real copyright here, but let's just call me !!3Kn5kfZkmkl
  18.  
  19. This program is free software: you can redistribute it and/or modify
  20. it under the terms of the GNU General Public License as published by
  21. the Free Software Foundation, either version 3 of the License, or
  22. (at your option) any later version.
  23.  
  24. This program is distributed in the hope that it will be useful,
  25. but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27. GNU General Public License for more details.
  28.  
  29. You should have received a copy of the GNU General Public License
  30. along with this program.  If not, see <http://www.gnu.org/licenses/>.
  31.  
  32. video timing/level info from:
  33.  - http://www.batsocks.co.uk/readme/video_timing.htm
  34.  - http://martin.hinner.info/vga/pal.html
  35.  - http://www.ni.com/white-paper/4750/en
  36.  - http://www.maximintegrated.com/app-notes/index.mvp/id/1184
  37.  - http://codeandlife.com/2012/07/31/realtime-composite-video-decoding-with-picoscope/
  38. */
  39.  
  40.  
  41.  
  42.  
  43. #include <stdio.h>
  44. #include <SDL.h>
  45. #include <SDL_opengl.h>
  46. #include <math.h>
  47.  
  48. //video signal is typically -40 to 120 IRE
  49. //this is a range of 160.
  50.  
  51. //opengl output isn't in a linear colorspace, but it's "close enough" for this kind of math to work...
  52.  
  53. //note that we're 40IRE higher than standards, because we can't go negative.
  54. #define SYNC_LEVEL  (0.0f  / 160.0f)
  55. #define BLANK_LEVEL (40.0f / 160.0f)
  56.  
  57. //if you're giving softcomp a 16...235 image, use the lower black level and higher white level.
  58. //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.
  59.     //#define BLACK_LEVEL (47.5f / 160.0f)
  60.     #define BLACK_LEVEL (40.0f / 160.0f)
  61.     //#define WHITE_LEVEL (140.0f/ 160.0f)
  62.     #define WHITE_LEVEL (160.0f / 160.0f)
  63.    
  64. #define NEUTRAL_LEVEL ((BLACK_LEVEL + WHITE_LEVEL)/2.0f)
  65.  
  66.  
  67. #define PICTURE_LINES 263
  68.  
  69. #define SIGNAL_WIDTH 384
  70.  
  71. #define ACTIVE_VIDEO_LINES 240
  72.  
  73.  
  74. //our signal geometry is aligned with the assumption that you can't get all PICTURE_LINES lines active out of your VGA card,
  75. //and that within those lines you can't get all SIGNAL_WIDTH pixels active.
  76.  
  77. //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.
  78. //our window goes in the upper-left corner of your display, potentially cutting off the bottom/right edges.
  79. //the VGA card typically outputs RGB=0v during its sync/blanking period, meaning that any cut-off image will be effectively 0 (sync!)
  80. //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
  81.  
  82. #define LINE_PERIOD 63.55 //63.4710743801652892561983471074380165289256192 //microseconds
  83.  
  84. #define HSYNC_WIDTH (4.7 / LINE_PERIOD) //hsync put at right edge of picture, so VGA card blanking doesn't kill it
  85.  
  86. #define H_FRONT_PORCH (1.5 / LINE_PERIOD)
  87. #define H_BACK_PORCH (4.5 / LINE_PERIOD)
  88.  
  89.  
  90. #define VSYNC_PULSE_WIDTH (27.1 / LINE_PERIOD) //"broad sync section"
  91. #define VSYNC_PULSE_LINES 3 //"broad sync" / "vertical sync pulses"
  92. #define VSYNC_LEVEL_LINES 3 //"pre-equalising pulses", "post-equalising pulses"
  93.  
  94. #define VSYNC_BLANK_LINES 10
  95.  
  96. const char *vertshader =
  97.     "varying vec4 color; \
  98.     void main()             \
  99.     {                   \
  100.         gl_Position = ftransform(); \
  101.         color = gl_Color;\
  102.         gl_TexCoord[0] = gl_MultiTexCoord0;\
  103.     }"
  104. ;
  105.  
  106.  
  107.  
  108. const char *fragshader =
  109.     "uniform sampler2D tex;         \
  110.     varying vec4 color;                 \
  111.     mat4 color_matrix =             \
  112.         mat4(               \
  113.             0.5,    -0.419, 0.081,  0.5,    \
  114.             0.299,  0.587,  0.144,  0.0,    \
  115.             -0.169, -0.331, 0.5,    0.5,    \
  116.             0.0,    0.0,    0.0,    1.0 \
  117.                             \
  118.         );                  \
  119.                             \
  120.     void main()             \
  121.     {                   \
  122.                         \
  123.         vec4 texturecolor = texture2D(tex,gl_TexCoord[0].xy);   \
  124.         gl_FragColor =  color*(texturecolor*color_matrix);  \
  125.     }"
  126. ;
  127.  
  128.  
  129.    
  130.  
  131. int main(int argc, const char **argv)
  132. {
  133.    
  134.    
  135.    
  136.     //init SDL and make screen surface in a borderless window
  137.     SDL_Init(SDL_INIT_VIDEO);
  138.     SDL_Surface *screen = SDL_SetVideoMode(SIGNAL_WIDTH, PICTURE_LINES, /*bpp*/0, SDL_NOFRAME | SDL_OPENGL);
  139.    
  140.    
  141.     //load test image into opengl
  142.     FILE *imagefile = fopen("lena256.raw", "rb");
  143.     unsigned char *imagedata = (unsigned char*)malloc(196608);
  144.     fread(imagedata, 1, 196608, imagefile);
  145.     GLuint imageTexture;
  146.    
  147.     glGenTextures(1, &imageTexture);
  148.     glBindTexture(GL_TEXTURE_2D, imageTexture);
  149.     glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
  150.     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, imagedata);
  151.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  152.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  153.     glBindTexture(GL_TEXTURE_2D, 0);   
  154.    
  155.    
  156.     //load shader into opengl - boilerplate code from NeHe (http://nehe.gamedev.net/article/glsl_an_introduction/25007/)
  157.     //and from lighthouse3d (http://www.lighthouse3d.com/tutorials/glsl-tutorial/troubleshooting-the-infolog/)
  158.     GLenum my_program;
  159.     GLenum my_vertex_shader;
  160.     GLenum my_fragment_shader;
  161.      
  162.     // Create Shader And Program Objects
  163.     my_program = glCreateProgramObjectARB();
  164.     my_vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
  165.     my_fragment_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
  166.      
  167.     // Load Shader Sources
  168.     glShaderSourceARB(my_vertex_shader, 1, &vertshader, NULL);
  169.     glShaderSourceARB(my_fragment_shader, 1, &fragshader, NULL);
  170.      
  171.     // Compile The Shaders
  172.     glCompileShaderARB(my_vertex_shader);
  173.     glCompileShaderARB(my_fragment_shader);
  174.      
  175.  
  176.     int infologLength = 0;
  177.     int charsWritten  = 0;
  178.     char *infoLog;
  179.  
  180.     glGetShaderiv(my_vertex_shader, GL_INFO_LOG_LENGTH,&infologLength);
  181.  
  182.     if (infologLength > 0)
  183.     {
  184.         infoLog = (char *)malloc(infologLength);
  185.         glGetShaderInfoLog(my_vertex_shader, infologLength, &charsWritten, infoLog);
  186.         printf("%s\n",infoLog);
  187.         free(infoLog);
  188.     }    
  189.  
  190.     infologLength = 0;
  191.     charsWritten  = 0;
  192.     glGetShaderiv(my_fragment_shader, GL_INFO_LOG_LENGTH,&infologLength);
  193.  
  194.     if (infologLength > 0)
  195.     {
  196.         infoLog = (char *)malloc(infologLength);
  197.         glGetShaderInfoLog(my_fragment_shader, infologLength, &charsWritten, infoLog);
  198.         printf("%s\n",infoLog);
  199.         free(infoLog);
  200.     }  
  201.    
  202.      
  203.     // Attach The Shader Objects To The Program Object
  204.     glAttachObjectARB(my_program, my_vertex_shader);
  205.     glAttachObjectARB(my_program, my_fragment_shader);
  206.      
  207.     // Link The Program Object
  208.     glLinkProgramARB(my_program);
  209.      
  210.  
  211.     infologLength = 0;
  212.     charsWritten  = 0;
  213.  
  214.     glGetProgramiv(my_program, GL_INFO_LOG_LENGTH,&infologLength);
  215.  
  216.     if (infologLength > 0)
  217.     {
  218.         infoLog = (char *)malloc(infologLength);
  219.         glGetProgramInfoLog(my_program, infologLength, &charsWritten, infoLog);
  220.         printf("%s\n",infoLog);
  221.         free(infoLog);
  222.     }
  223.  
  224.    
  225.    
  226.     //set up basic GL properties
  227.    
  228.     glMatrixMode(GL_PROJECTION);
  229.     glLoadIdentity();
  230.     glOrtho(0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f); //raster coordinates from 0,0 (top-left) to 1,1.
  231.    
  232.     glMatrixMode(GL_MODELVIEW);
  233.     glLoadIdentity();
  234.    
  235.     //printf("%f\n", (1.0f - (H_BACK_PORCH+H_FRONT_PORCH+HSYNC_WIDTH)) / (COLORBURST_PERIOD));
  236.    
  237.     //main loop
  238.     int done = 0;
  239.    
  240.     int framenum = 0;
  241.    
  242.     while(!done)
  243.     {
  244.         //clear whole signal to blanking level
  245.         glViewport(0,0,SIGNAL_WIDTH,263);
  246.         glClearColor(NEUTRAL_LEVEL,BLANK_LEVEL,NEUTRAL_LEVEL,1.0f);
  247.         glClear(GL_COLOR_BUFFER_BIT);
  248.            
  249.         //horizontal pulse on right edge of picture until vsync
  250.  
  251.         glViewport(0,(VSYNC_LEVEL_LINES+VSYNC_PULSE_LINES+VSYNC_LEVEL_LINES),SIGNAL_WIDTH,PICTURE_LINES-(VSYNC_LEVEL_LINES+VSYNC_PULSE_LINES+VSYNC_LEVEL_LINES));
  252.        
  253.         glColor3f(SYNC_LEVEL, SYNC_LEVEL, SYNC_LEVEL);
  254.         glBegin(GL_QUADS);
  255.             glVertex3f(1.0f, 0.0f, -1.0f);
  256.             glVertex3f(1.0f, 1.0f, -1.0f);
  257.             glVertex3f(1.0f - HSYNC_WIDTH, 1.0f, -1.0f);
  258.             glVertex3f(1.0f - HSYNC_WIDTH, 0.0f, -1.0f);
  259.         glEnd();
  260.                
  261.        
  262.         //bring active video area up to black level
  263.         glViewport(0,(PICTURE_LINES-ACTIVE_VIDEO_LINES) - VSYNC_BLANK_LINES, SIGNAL_WIDTH, ACTIVE_VIDEO_LINES);
  264.        
  265.         glColor3f(BLACK_LEVEL, BLACK_LEVEL, BLACK_LEVEL);
  266.         glBegin(GL_QUADS);
  267.             glVertex3f(H_BACK_PORCH, 0.0f, -1.0f);
  268.             glVertex3f(H_BACK_PORCH, 1.0f, -1.0f);
  269.             glVertex3f(1.0f - (H_FRONT_PORCH+HSYNC_WIDTH), 1.0f, -1.0f);
  270.             glVertex3f(1.0f - (H_FRONT_PORCH+HSYNC_WIDTH), 0.0f, -1.0f);
  271.         glEnd();
  272.        
  273.        
  274.        
  275.         //create RGB->YUV matrix - use A(W) component (always 1 in source image) to bias U and V
  276.         /*float color_matrix[16] =
  277.         {
  278.             0.299,  0.587,  0.144,  0.0,
  279.             -0.169, -0.331, 0.5,    0.5,
  280.             0.5,    -0.419, 0.081,  0.5,
  281.             0.0,    0.0,    0.0,    1.0
  282.            
  283.         };*/
  284.         /*float color_matrix[16] =
  285.         {
  286.             0.299, -0.169, 0.5, 0.0,
  287.             0.587, -0.331, -0.419, 0.0,
  288.             0.144, 0.5, 0.081, 0.0,
  289.             0.0, 0.5, 0.5, 1.0
  290.            
  291.         };*/
  292.         //glMatrixMode(GL_COLOR);
  293.         //glLoadTransposeMatrixf(color_matrix);
  294.        
  295.        
  296.         //it couldn't have been that easy, huh?
  297.         //use shader.
  298.    
  299.         glUseProgramObjectARB(my_program); 
  300.        
  301.        
  302.         //whole picture is at black level - bring potentially up to white level with additive blending
  303.         //blend picture in using YUV color transform
  304.        
  305.         glEnable(GL_BLEND);
  306.         glBlendFunc(GL_ONE, GL_ONE);
  307.         glEnable(GL_TEXTURE_2D);
  308.         glBindTexture(GL_TEXTURE_2D, imageTexture);
  309.         glColor3f( WHITE_LEVEL-BLANK_LEVEL, WHITE_LEVEL-BLANK_LEVEL, WHITE_LEVEL-BLANK_LEVEL);
  310.         glBegin(GL_QUADS);
  311.             glTexCoord2f(0.0f,0.0f);
  312.             glVertex3f(H_BACK_PORCH, 0.0f, -1.0f);
  313.            
  314.             glTexCoord2f(0.0f,1.0f);
  315.             glVertex3f(H_BACK_PORCH, 1.0f, -1.0f);
  316.            
  317.             glTexCoord2f(1.0f,1.0f);
  318.             glVertex3f(1.0f - (H_FRONT_PORCH+HSYNC_WIDTH), 1.0f, -1.0f);
  319.            
  320.             glTexCoord2f(1.0f,0.0f);
  321.             glVertex3f(1.0f - (H_FRONT_PORCH+HSYNC_WIDTH), 0.0f, -1.0f);
  322.         glEnd();
  323.        
  324.         glDisable(GL_TEXTURE_2D);
  325.         glDisable(GL_BLEND);       
  326.  
  327.         //glLoadIdentity();
  328.         glUseProgramObjectARB(0);  
  329.        
  330.        
  331.         //vsync goes at bottom of picture, because it will likely be clobbered by your video card blanking
  332.        
  333.         //pre-equalising pulses
  334.         int i;
  335.         for(i=0;i<VSYNC_LEVEL_LINES;i++)
  336.         {
  337.        
  338.             glViewport(0,i+(VSYNC_LEVEL_LINES+VSYNC_PULSE_LINES),SIGNAL_WIDTH,1);
  339.            
  340.             glColor3f(SYNC_LEVEL, SYNC_LEVEL, SYNC_LEVEL);
  341.             glBegin(GL_QUADS);
  342.                 glVertex3f(1.0f, 0.0f, -1.0f);
  343.                 glVertex3f(1.0f, 1.0f, -1.0f);
  344.                 glVertex3f(1.0f-HSYNC_WIDTH, 1.0f, -1.0f);
  345.                 glVertex3f(1.0f-HSYNC_WIDTH, 0.0f, -1.0f);
  346.            
  347.                 glVertex3f(0.5f, 0.0f, -1.0f);
  348.                 glVertex3f(0.5f, 1.0f, -1.0f);
  349.                 glVertex3f(0.5f - HSYNC_WIDTH, 1.0f, -1.0f);
  350.                 glVertex3f(0.5f -  HSYNC_WIDTH, 0.0f, -1.0f);
  351.            
  352.             glEnd();
  353.         }
  354.        
  355.        
  356.        
  357.        
  358.         //sync pulses
  359.        
  360.         for(i=0;i<VSYNC_PULSE_LINES;i++)
  361.         {
  362.        
  363.             glViewport(0,i+(VSYNC_LEVEL_LINES),SIGNAL_WIDTH,1);
  364.            
  365.             glColor3f(SYNC_LEVEL, SYNC_LEVEL, SYNC_LEVEL);
  366.             glBegin(GL_QUADS);
  367.                 glVertex3f(1.0f, 0.0f, -1.0f);
  368.                 glVertex3f(1.0f, 1.0f, -1.0f);
  369.                 glVertex3f(1.0f-VSYNC_PULSE_WIDTH, 1.0f, -1.0f);
  370.                 glVertex3f(1.0f-VSYNC_PULSE_WIDTH, 0.0f, -1.0f);
  371.            
  372.                 glVertex3f(0.5f, 0.0f, -1.0f);
  373.                 glVertex3f(0.5f, 1.0f, -1.0f);
  374.                 glVertex3f(0.5f - VSYNC_PULSE_WIDTH, 1.0f, -1.0f);
  375.                 glVertex3f(0.5f -  VSYNC_PULSE_WIDTH, 0.0f, -1.0f);
  376.            
  377.             glEnd();
  378.         }
  379.        
  380.        
  381.        
  382.         //post-equalizing pulses
  383.         for(i=0;i<VSYNC_LEVEL_LINES;i++)
  384.         {
  385.             glViewport(0,i,SIGNAL_WIDTH,1);
  386.            
  387.             glColor3f(SYNC_LEVEL, SYNC_LEVEL, SYNC_LEVEL);
  388.             glBegin(GL_QUADS);
  389.                 glVertex3f(1.0f, 0.0f, -1.0f);
  390.                 glVertex3f(1.0f, 1.0f, -1.0f);
  391.                 glVertex3f(1.0f - HSYNC_WIDTH, 1.0f, -1.0f);
  392.                 glVertex3f(1.0f - HSYNC_WIDTH, 0.0f, -1.0f);
  393.            
  394.            
  395.                 glVertex3f(0.5f, 0.0f, -1.0f);
  396.                 glVertex3f(0.5f, 1.0f, -1.0f);
  397.                 glVertex3f(0.5f - HSYNC_WIDTH, 1.0f, -1.0f);
  398.                 glVertex3f(0.5f - HSYNC_WIDTH, 0.0f, -1.0f);
  399.             glEnd();
  400.            
  401.            
  402.        
  403.         }
  404.        
  405.  
  406.        
  407.         //page-flip, displaying the new image
  408.         SDL_GL_SwapBuffers();
  409.        
  410.        
  411.         //poll input, break main loop if there's a quit event
  412.         SDL_Event event;
  413.         while(SDL_PollEvent(&event))
  414.         {
  415.             if(event.type == SDL_QUIT)
  416.                 done = 1;
  417.         }
  418.        
  419.         framenum++;
  420.     }
  421.    
  422.    
  423.    
  424.     return 0;
  425. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement