GLx4 w/h mult of 4 is not req, cwdw crop input range checks, fix cropp.png dir, chang...
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / vframe3d.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2011 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #define GL_GLEXT_PROTOTYPES
23
24 #include "bcpbuffer.h"
25 #include "bcresources.h"
26 #include "bcsignals.h"
27 #include "bcsynchronous.h"
28 #include "bctexture.h"
29 #include "bcwindowbase.h"
30 #include "vframe.h"
31
32 #ifdef HAVE_GL
33 #include <GL/gl.h>
34 #include <GL/glext.h>
35 #endif
36
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 int VFrame::get_opengl_state()
43 {
44         return opengl_state;
45 }
46
47 void VFrame::set_opengl_state(int value)
48 {
49         opengl_state = value;
50 }
51
52 int VFrame::get_window_id()
53 {
54         return texture ? texture->window_id : -1;
55 }
56
57 int VFrame::get_texture_id()
58 {
59         return texture ? texture->texture_id : -1;
60 }
61
62 int VFrame::get_texture_w()
63 {
64         return texture ? texture->texture_w : 0;
65 }
66
67 int VFrame::get_texture_h()
68 {
69         return texture ? texture->texture_h : 0;
70 }
71
72
73 int VFrame::get_texture_components()
74 {
75         return texture ? texture->texture_components : 0;
76 }
77
78
79
80
81
82
83
84
85
86
87
88 void VFrame::to_texture()
89 {
90 #ifdef HAVE_GL
91
92 // Must be here so user can create textures without copying data by setting
93 // opengl_state to TEXTURE.
94         BC_Texture::new_texture(&texture, get_w(), get_h(), get_color_model());
95
96 // Determine what to do based on state
97         switch(opengl_state) {
98         case VFrame::TEXTURE:
99                 return;
100
101         case VFrame::SCREEN:
102                 if(pbuffer) {
103                         enable_opengl();
104                         screen_to_texture();
105                 }
106                 opengl_state = VFrame::TEXTURE;
107                 return;
108         }
109
110 //printf("VFrame::to_texture %d\n", texture_id);
111         GLenum type, format;
112         switch(color_model) {
113         case BC_RGB888:
114         case BC_YUV888:
115                 type = GL_UNSIGNED_BYTE;
116                 format = GL_RGB;
117                 break;
118
119         case BC_RGBA8888:
120         case BC_YUVA8888:
121                 type = GL_UNSIGNED_BYTE;
122                 format = GL_RGBA;
123                 break;
124
125         case BC_RGB_FLOAT:
126                 type = GL_FLOAT;
127                 format = GL_RGB;
128                 break;
129
130         case BC_RGBA_FLOAT:
131                 format = GL_RGBA;
132                 type = GL_FLOAT;
133                 break;
134
135         default:
136                 fprintf(stderr,
137                         "VFrame::to_texture: unsupported color model %d.\n",
138                         color_model);
139                 return;
140         }
141
142         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, get_w(), get_h(),
143                 format, type, get_rows()[0]);
144         opengl_state = VFrame::TEXTURE;
145 #endif
146 }
147
148 void VFrame::create_pbuffer()
149 {
150 #ifdef GLx4
151         int ww = (get_w()+3) & ~3, hh = (get_h()+3) & ~3;
152 #else
153         int ww = get_w(), hh = get_h();
154 #endif
155         if( pbuffer && (pbuffer->w != ww || pbuffer->h != hh ||
156             pbuffer->window_id != BC_WindowBase::get_synchronous()->current_window->get_id() ) ) {
157                 delete pbuffer;
158                 pbuffer = 0;
159         }
160
161         if( !pbuffer ) {
162                 pbuffer = new BC_PBuffer(ww, hh);
163         }
164 }
165
166 void VFrame::enable_opengl()
167 {
168         create_pbuffer();
169         if( pbuffer ) {
170                 pbuffer->enable_opengl();
171         }
172 }
173
174 BC_PBuffer* VFrame::get_pbuffer()
175 {
176         return pbuffer;
177 }
178
179
180 void VFrame::screen_to_texture(int x, int y, int w, int h)
181 {
182 #ifdef HAVE_GL
183 // Create texture
184         BC_Texture::new_texture(&texture,
185                 get_w(), get_h(), get_color_model());
186         if(pbuffer) {
187                 glEnable(GL_TEXTURE_2D);
188
189 // Read canvas into texture, use back texture for DOUBLE_BUFFER
190 #if 1
191 // According to the man page, it must be GL_BACK for the onscreen buffer
192 // and GL_FRONT for a single buffered PBuffer.  In reality it must be
193 // GL_BACK for a single buffered PBuffer if the PBuffer has alpha and using
194 // GL_FRONT captures the onscreen front buffer.
195 // 10/11/2010 is now generating "illegal operation"
196                 glReadBuffer(GL_BACK);
197 #else
198                 glReadBuffer(BC_WindowBase::get_synchronous()->is_pbuffer ?
199                         GL_FRONT : GL_BACK);
200 #endif
201                 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
202                         x >= 0 ? x : 0, y >= 0 ? y : 0,
203                         w >= 0 ? w : get_w(), h >= 0 ? h : get_h());
204         }
205 #endif
206 }
207
208 void VFrame::screen_to_ram()
209 {
210 #ifdef HAVE_GL
211         enable_opengl();
212         glReadBuffer(GL_BACK);
213         int type = BC_CModels::is_float(color_model) ? GL_FLOAT : GL_UNSIGNED_BYTE;
214         int format = BC_CModels::has_alpha(color_model) ? GL_RGBA : GL_RGB;
215         glReadPixels(0, 0, get_w(), get_h(), format, type, get_rows()[0]);
216         opengl_state = VFrame::RAM;
217 #endif
218 }
219
220 void VFrame::draw_texture(
221         float in_x1, float in_y1, float in_x2, float in_y2,
222         float out_x1, float out_y1, float out_x2, float out_y2,
223         int flip_y)
224 {
225 #ifdef HAVE_GL
226         in_x1 /= get_texture_w();  in_y1 /= get_texture_h();
227         in_x2 /= get_texture_w();  in_y2 /= get_texture_h();
228         float ot_y1 = flip_y ? -out_y1 : -out_y2;
229         float ot_y2 = flip_y ? -out_y2 : -out_y1;
230         texture->draw_texture(
231                 in_x1,in_y1,  in_x2,in_y2,
232                 out_x1,ot_y1, out_x2, ot_y2);
233 #endif
234 }
235
236 void VFrame::draw_texture(int flip_y)
237 {
238         draw_texture(0,0,  get_w(),get_h(),
239                 0,0, get_w(),get_h(), flip_y);
240 }
241
242
243 void VFrame::bind_texture(int texture_unit, int nearest)
244 {
245 // Bind the texture
246         if(texture) {
247                 texture->bind(texture_unit, nearest);
248         }
249 }
250
251
252 void VFrame::init_screen(int w, int h)
253 {
254 #ifdef HAVE_GL
255         glViewport(0, 0, w, h);
256         glMatrixMode(GL_PROJECTION);
257         glLoadIdentity();
258         float near = 1;
259         float far = 100;
260         float frustum_ratio = near / ((near + far)/2);
261         float near_h = (float)h * frustum_ratio;
262         float near_w = (float)w * frustum_ratio;
263         glFrustum(-near_w/2, near_w/2, -near_h/2, near_h/2, near, far);
264         glMatrixMode(GL_MODELVIEW);
265         glLoadIdentity();
266 // Shift down and right so 0,0 is the top left corner
267         glTranslatef(-w/2.f, h/2.f, 0.f);
268         glTranslatef(0.0, 0.0, -(far + near) / 2);
269
270         glDisable(GL_DEPTH_TEST);
271         glShadeModel(GL_SMOOTH);
272 // Default for direct copy playback
273         glDisable(GL_BLEND);
274         glDisable(GL_COLOR_MATERIAL);
275         glDisable(GL_CULL_FACE);
276         glEnable(GL_NORMALIZE);
277         glAlphaFunc(GL_ALWAYS, 0);
278         glDisable(GL_ALPHA_TEST);
279         glDisable(GL_LIGHTING);
280
281         const GLfloat zero[] = { 0, 0, 0, 0 };
282 //      const GLfloat one[] = { 1, 1, 1, 1 };
283 //      const GLfloat light_position[] = { 0, 0, -1, 0 };
284 //      const GLfloat light_direction[] = { 0, 0, 1, 0 };
285
286 //      glEnable(GL_LIGHT0);
287 //      glLightfv(GL_LIGHT0, GL_AMBIENT, zero);
288 //      glLightfv(GL_LIGHT0, GL_DIFFUSE, one);
289 //      glLightfv(GL_LIGHT0, GL_SPECULAR, one);
290 //      glLighti(GL_LIGHT0, GL_SPOT_CUTOFF, 180);
291 //      glLightfv(GL_LIGHT0, GL_POSITION, light_position);
292 //      glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_direction);
293 //      glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
294 //      glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zero);
295         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, zero);
296         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, zero);
297         glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, zero);
298         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, zero);
299         glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0);
300 #ifndef GLx4
301         glPixelStorei(GL_PACK_ALIGNMENT,1);
302         glPixelStorei(GL_UNPACK_ALIGNMENT,1);
303 #endif
304 #endif
305 }
306
307 void VFrame::init_screen()
308 {
309         init_screen(get_w(), get_h());
310 }
311
312
313
314 #ifdef HAVE_GL
315
316 static int print_error(const char *text, unsigned int object, int is_program)
317 {
318         char info[BCTEXTLEN];
319         int len = 0;
320         if( is_program )
321                 glGetProgramInfoLog(object, BCTEXTLEN, &len, info);
322         else
323                 glGetShaderInfoLog(object, BCTEXTLEN, &len, info);
324         if( len > 0 ) printf("Playback3D::print_error:\n%s\n%s\n", text, info);
325         return !len ? 0 : 1;
326 }
327
328 static char *shader_segs(const char **segs, int n)
329 {
330         if( !segs || !n ) return 0;
331 // concat source segs
332         int ids = 0;
333         char *ret = 0;
334         for( int i=0; i<n; ++i ) {
335                 const char *text = *segs++;
336                 char src[strlen(text) + BCSTRLEN + 1], *sp = src;
337                 const char *tp = strstr(text, "main()");
338                 if( tp ) {
339 // Replace main() with a mainxxx()
340                         int n = tp - text;
341                         memcpy(sp, text, n);  sp += n;
342                         sp += sprintf(sp, "main%03d()", ids++);
343                         strcpy(sp, tp+strlen("main()"));
344                         text = src;
345                 }
346                 char *cp = !ret ? cstrdup(text) : cstrcat(2, ret, text);
347                 delete [] ret;  ret = cp;
348         }
349
350 // add main() which calls mainxxx() in order
351         char main_prog[BCTEXTLEN];
352         char *cp = main_prog;
353         cp += sprintf(cp, "\nvoid main() {\n");
354         for( int i=0; i < ids; ++i )
355                 cp += sprintf(cp, "\tmain%03d();\n", i);
356         cp += sprintf(cp, "}\n");
357         if( ret ) {
358                 cp = cstrcat(2, ret, main_prog);
359                 delete [] ret;  ret = cp;
360         }
361         else
362                 ret = cstrdup(main_prog);
363         return ret;
364 }
365
366 static int compile_shader(unsigned int &shader, int type, const GLchar *text)
367 {
368         shader = glCreateShader(type);
369         glShaderSource(shader, 1, &text, 0);
370         glCompileShader(shader);
371         return print_error(text, shader, 0);
372 }
373
374 static unsigned int build_shader(const char *vert, const char *frag)
375 {
376         int error = 0;
377         unsigned int vertex_shader = 0;
378         unsigned int fragment_shader = 0;
379         unsigned int program = glCreateProgram();
380         if( !error && vert )
381                 error = compile_shader(vertex_shader, GL_VERTEX_SHADER, vert);
382         if( !error && frag )
383                 error = compile_shader(fragment_shader, GL_FRAGMENT_SHADER, frag);
384         if( !error && vert ) glAttachShader(program, vertex_shader);
385         if( !error && frag ) glAttachShader(program, fragment_shader);
386         if( !error ) glLinkProgram(program);
387         if( !error ) error = print_error("link", program, 1);
388         if( !error )
389                 BC_WindowBase::get_synchronous()->put_shader(program, vert, frag);
390         else {
391                 glDeleteProgram(program);
392                 program = 0;
393         }
394         return program;
395 }
396
397 #endif
398
399 // call as:
400 //    make_shader(0, seg1, .., segn, 0);
401 // or make_shader(&seg);
402 // line 1: optional comment // vertex shader
403
404 unsigned int VFrame::make_shader(const char **segments, ...)
405 {
406         unsigned int program = 0;
407 #ifdef HAVE_GL
408 // Construct single source file out of arguments
409         int nb_segs = 0;
410         if( !segments ) {  // arg list
411                 va_list list;  va_start(list, segments);
412                 while( va_arg(list, char*) != 0 ) ++nb_segs;
413                 va_end(list);
414         }
415         else { // segment list
416                 while( segments[nb_segs] ) ++nb_segs;
417         }
418         const char *segs[++nb_segs];
419         if( !segments ) {
420                 va_list list;  va_start(list, segments);
421                 for( int i=0; i<nb_segs; ++i )
422                         segs[i] = va_arg(list, const char *);
423                 va_end(list);
424                 segments = segs;
425         }
426
427         const char *vert_shaders[nb_segs];  int vert_segs = 0;
428         const char *frag_shaders[nb_segs];  int frag_segs = 0;
429         for( int i=0; segments[i]!=0; ++i ) {
430                 const char *seg = segments[i];
431                 if( strstr(seg, "// vertex shader") )
432                         vert_shaders[vert_segs++] = seg;
433                 else
434                         frag_shaders[frag_segs++] = seg;
435         }
436
437         char *vert = shader_segs(vert_shaders, vert_segs);
438         char *frag = shader_segs(frag_shaders, frag_segs);
439         if( !BC_WindowBase::get_synchronous()->get_shader(&program, vert, frag) )
440                 program = build_shader(vert, frag);
441         delete [] vert;
442         delete [] frag;
443 #endif
444         return program;
445 }
446
447 void VFrame::dump_shader(int shader_id)
448 {
449         BC_WindowBase::get_synchronous()->dump_shader(shader_id);
450 }
451
452
453 void VFrame::clear_pbuffer()
454 {
455 #ifdef HAVE_GL
456         int rgb = clear_color>=0 ? clear_color : 0;
457         int a = clear_color>=0 ? clear_alpha : 0;
458         int r = (rgb>>16) & 0xff;
459         int g = (rgb>> 8) & 0xff;
460         int b = (rgb>> 0) & 0xff;
461         if( BC_CModels::is_yuv(get_color_model()) )
462                 YUV::yuv.rgb_to_yuv_8(r, g, b);
463         glClearColor(r/255.f, g/255.f, b/255.f, a/255.f);
464         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
465 #endif
466 }
467