comparison libvo/gl_common.c @ 16488:3191dcb27a12

hardware color-space conversion for vo_gl and vo_gl2
author reimar
date Wed, 14 Sep 2005 22:08:04 +0000
parents 01d27e023ae4
children cbd0ee58633b
comparison
equal deleted inserted replaced
16487:bafefab46f12 16488:3191dcb27a12
1 /**
2 * Common OpenGL routines.
3 * Copyleft (C) Reimar Döffinger <Reimar.Doeffinger@stud.uni-karlsruhe.de>, 2005
4 * Licensend under the GNU GPL v2.
5 * Special thanks go to the xine team and Matthias Hopf, whose video_out_opengl.c
6 * gave me lots of good ideas.
7 */
1 #include <stdlib.h> 8 #include <stdlib.h>
9 #include <stdio.h>
2 #include <string.h> 10 #include <string.h>
11 #include <math.h>
3 #include "gl_common.h" 12 #include "gl_common.h"
4 13
5 void (APIENTRY *GenBuffers)(GLsizei, GLuint *); 14 void (APIENTRY *GenBuffers)(GLsizei, GLuint *);
6 void (APIENTRY *DeleteBuffers)(GLsizei, const GLuint *); 15 void (APIENTRY *DeleteBuffers)(GLsizei, const GLuint *);
7 void (APIENTRY *BindBuffer)(GLenum, GLuint); 16 void (APIENTRY *BindBuffer)(GLenum, GLuint);
16 GLenum, GLenum, GLboolean, GLboolean, 25 GLenum, GLenum, GLboolean, GLboolean,
17 GLboolean); 26 GLboolean);
18 void (APIENTRY *ActiveTexture)(GLenum); 27 void (APIENTRY *ActiveTexture)(GLenum);
19 void (APIENTRY *BindTexture)(GLenum, GLuint); 28 void (APIENTRY *BindTexture)(GLenum, GLuint);
20 void (APIENTRY *MultiTexCoord2f)(GLenum, GLfloat, GLfloat); 29 void (APIENTRY *MultiTexCoord2f)(GLenum, GLfloat, GLfloat);
30 void (APIENTRY *GenPrograms)(GLsizei, GLuint *);
31 void (APIENTRY *DeletePrograms)(GLsizei, const GLuint *);
21 void (APIENTRY *BindProgram)(GLenum, GLuint); 32 void (APIENTRY *BindProgram)(GLenum, GLuint);
22 void (APIENTRY *ProgramString)(GLenum, GLenum, GLsizei, const GLvoid *); 33 void (APIENTRY *ProgramString)(GLenum, GLenum, GLsizei, const GLvoid *);
23 void (APIENTRY *ProgramEnvParameter4f)(GLenum, GLuint, GLfloat, GLfloat, 34 void (APIENTRY *ProgramEnvParameter4f)(GLenum, GLuint, GLfloat, GLfloat,
24 GLfloat, GLfloat); 35 GLfloat, GLfloat);
25 int (APIENTRY *SwapInterval)(int); 36 int (APIENTRY *SwapInterval)(int);
247 if (!BindTexture) 258 if (!BindTexture)
248 BindTexture = getProcAddress("glBindTextureARB"); 259 BindTexture = getProcAddress("glBindTextureARB");
249 MultiTexCoord2f = getProcAddress("glMultiTexCoord2f"); 260 MultiTexCoord2f = getProcAddress("glMultiTexCoord2f");
250 if (!MultiTexCoord2f) 261 if (!MultiTexCoord2f)
251 MultiTexCoord2f = getProcAddress("glMultiTexCoord2fARB"); 262 MultiTexCoord2f = getProcAddress("glMultiTexCoord2fARB");
263 GenPrograms = getProcAddress("glGenPrograms");
264 if (!GenPrograms)
265 GenPrograms = getProcAddress("glGenProgramsARB");
266 if (!GenPrograms)
267 GenPrograms = getProcAddress("glGenProgramsNV");
268 DeletePrograms = getProcAddress("glDeletePrograms");
269 if (!DeletePrograms)
270 DeletePrograms = getProcAddress("glDeleteProgramsARB");
271 if (!DeletePrograms)
272 DeletePrograms = getProcAddress("glDeleteProgramsNV");
252 BindProgram = getProcAddress("glBindProgram"); 273 BindProgram = getProcAddress("glBindProgram");
253 if (!BindProgram) 274 if (!BindProgram)
254 BindProgram = getProcAddress("glBindProgramARB"); 275 BindProgram = getProcAddress("glBindProgramARB");
255 if (!BindProgram) 276 if (!BindProgram)
256 BindProgram = getProcAddress("glBindProgramNV"); 277 BindProgram = getProcAddress("glBindProgramNV");
372 if (y < y_max) 393 if (y < y_max)
373 glTexSubImage2D(target, 0, x, y, w, y_max - y, format, type, data); 394 glTexSubImage2D(target, 0, x, y, w, y_max - y, format, type, data);
374 } 395 }
375 396
376 /** 397 /**
398 * \brief Setup register combiners for YUV to RGB conversion.
399 * \param uvcos used for saturation and hue adjustment
400 * \param uvsin used for saturation and hue adjustment
401 */
402 static void glSetupYUVCombiners(float uvcos, float uvsin) {
403 GLfloat ucoef[4];
404 GLfloat vcoef[4];
405 GLint i;
406 glGetIntegerv(GL_MAX_GENERAL_COMBINERS_NV, &i);
407 if (i < 2)
408 mp_msg(MSGT_VO, MSGL_ERR,
409 "[gl] 2 general combiners needed for YUV combiner support (found %i)\n", i);
410 glGetIntegerv (GL_MAX_TEXTURE_UNITS, &i);
411 if (i < 3)
412 mp_msg(MSGT_VO, MSGL_ERR,
413 "[gl] 3 texture units needed for YUV combiner support (found %i)\n", i);
414 if (!CombinerInput || !CombinerOutput ||
415 !CombinerParameterfv || !CombinerParameteri) {
416 mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Combiner functions missing!\n");
417 return;
418 }
419 ucoef[0] = 0 * uvcos + 1.403 * uvsin;
420 vcoef[0] = 0 * uvsin + 1.403 * uvcos;
421 ucoef[1] = -0.344 * uvcos + -0.714 * uvsin;
422 vcoef[1] = -0.344 * uvsin + -0.714 * uvcos;
423 ucoef[2] = 1.770 * uvcos + 0 * uvsin;
424 vcoef[2] = 1.770 * uvsin + 0 * uvcos;
425 ucoef[3] = 0;
426 vcoef[3] = 0;
427 // Coefficients (probably) must be in [0, 1] range, whereas they originally
428 // are in [-2, 2] range, so here comes the trick:
429 // First put them in the [-0.5, 0.5] range, then add 0.5.
430 // This can be undone with the HALF_BIAS and SCALE_BY_FOUR arguments
431 // for CombinerInput and CombinerOutput
432 for (i = 0; i < 4; i++) {
433 ucoef[i] = ucoef[i] * 0.25 + 0.5;
434 vcoef[i] = vcoef[i] * 0.25 + 0.5;
435 }
436 CombinerParameterfv(GL_CONSTANT_COLOR0_NV, ucoef);
437 CombinerParameterfv(GL_CONSTANT_COLOR1_NV, vcoef);
438
439 // UV first, like this green component cannot overflow
440 CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
441 GL_TEXTURE1, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
442 CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
443 GL_CONSTANT_COLOR0_NV, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
444 CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV,
445 GL_TEXTURE2, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
446 CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV,
447 GL_CONSTANT_COLOR1_NV, GL_HALF_BIAS_NORMAL_NV, GL_RGB);
448 CombinerOutput(GL_COMBINER0_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV,
449 GL_SPARE0_NV, GL_SCALE_BY_FOUR_NV, GL_NONE, GL_FALSE,
450 GL_FALSE, GL_FALSE);
451
452 // stage 2
453 CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV, GL_SPARE0_NV,
454 GL_SIGNED_IDENTITY_NV, GL_RGB);
455 CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV, GL_ZERO,
456 GL_UNSIGNED_INVERT_NV, GL_RGB);
457 CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_C_NV,
458 GL_TEXTURE0, GL_SIGNED_IDENTITY_NV, GL_RGB);
459 CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO,
460 GL_UNSIGNED_INVERT_NV, GL_RGB);
461 CombinerOutput(GL_COMBINER1_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV,
462 GL_SPARE0_NV, GL_NONE, GL_NONE, GL_FALSE,
463 GL_FALSE, GL_FALSE);
464
465 // leave final combiner stage in default mode
466 CombinerParameteri(GL_NUM_GENERAL_COMBINERS_NV, 2);
467 }
468
469 static const char *yuv_prog_template =
470 "!!ARBfp1.0\n"
471 "TEMP res, y, u, v;"
472 "TEX y, fragment.texcoord[0], texture[0], 2D;"
473 "MAD res, y, {%.4f, %.4f, %.4f}, {%.4f, %.4f, %.4f};"
474 "TEX u, fragment.texcoord[1], texture[1], 2D;"
475 "MAD res, u, {%.4f, %.4f, %.4f}, res;"
476 "TEX v, fragment.texcoord[2], texture[2], 2D;"
477 "MAD result.color, v, {%.4f, %.4f, %.4f}, res;"
478 "END";
479
480 static const char *yuv_pow_prog_template =
481 "!!ARBfp1.0\n"
482 "TEMP res, y, u, v;"
483 "TEX y, fragment.texcoord[0], texture[0], 2D;"
484 "MAD res, y, {%.4f, %.4f, %.4f}, {%.4f, %.4f, %.4f};"
485 "TEX u, fragment.texcoord[1], texture[1], 2D;"
486 "MAD res, u, {%.4f, %.4f, %.4f}, res;"
487 "TEX v, fragment.texcoord[2], texture[2], 2D;"
488 "MAD_SAT res, v, {%.4f, %.4f, %.4f}, res;"
489 "POW result.color.r, res.r, %.4f.r;"
490 "POW result.color.g, res.g, %.4f.g;"
491 "POW result.color.b, res.b, %.4f.b;"
492 "END";
493
494 static const char *yuv_lookup_prog_template =
495 "!!ARBfp1.0\n"
496 "TEMP res, y, u, v;"
497 "TEX y, fragment.texcoord[0], texture[0], 2D;"
498 "MAD res, y, {%.4f, %.4f, %.4f, 0}, {%.4f, %.4f, %.4f, 0};"
499 "TEX u, fragment.texcoord[1], texture[1], 2D;"
500 "MAD res, u, {%.4f, %.4f, %.4f, 0}, res;"
501 "TEX v, fragment.texcoord[2], texture[2], 2D;"
502 "MAD res, v, {%.4f, %.4f, %.4f, 0}, res;"
503 "ADD res.a, res.a, 0.125;"
504 "TEX result.color.r, res.raaa, texture[3], 2D;"
505 "ADD res.a, res.a, 0.25;"
506 "TEX result.color.g, res.gaaa, texture[3], 2D;"
507 "ADD res.a, res.a, 0.25;"
508 "TEX result.color.b, res.baaa, texture[3], 2D;"
509 "END";
510
511 /**
512 * \brief setup a fragment program that will do YUV->RGB conversion
513 * \param brightness brightness adjustment offset
514 * \param contrast contrast adjustment factor
515 * \param uvcos used for saturation and hue adjustment
516 * \param uvsin used for saturation and hue adjustment
517 * \param lookup use fragment program that uses texture unit 4 to
518 * do additional conversion via lookup.
519 */
520 static void glSetupYUVFragprog(float brightness, float contrast,
521 float uvcos, float uvsin, float rgamma,
522 float ggamma, float bgamma, int type) {
523 char yuv_prog[1000];
524 const char *prog_template = yuv_prog_template;
525 int lookup = 0;
526 GLint i;
527 // this is the conversion matrix, with y, u, v factors
528 // for red, green, blue and the constant offsets
529 float ry, ru, rv, rc;
530 float gy, gu, gv, gc;
531 float by, bu, bv, bc;
532 switch (type) {
533 case YUV_CONVERSION_FRAGMENT_POW:
534 prog_template = yuv_pow_prog_template;
535 break;
536 case YUV_CONVERSION_FRAGMENT_LOOKUP:
537 prog_template = yuv_lookup_prog_template;
538 lookup = 1;
539 break;
540 }
541 glGetIntegerv (GL_MAX_TEXTURE_UNITS, &i);
542 if (i < 3)
543 mp_msg(MSGT_VO, MSGL_ERR,
544 "[gl] 3 texture units needed for YUV fragment support (found %i)\n", i);
545 if (lookup && i < 4)
546 mp_msg(MSGT_VO, MSGL_ERR,
547 "[gl] 4 texture units needed for YUV fragment support with lookup (found %i)\n", i);
548 if (!ProgramString) {
549 mp_msg(MSGT_VO, MSGL_FATAL, "[gl] ProgramString function missing!\n");
550 return;
551 }
552 ry = 1.164 * contrast;
553 gy = 1.164 * contrast;
554 by = 1.164 * contrast;
555 ru = 0 * uvcos + 1.596 * uvsin;
556 rv = 0 * uvsin + 1.596 * uvcos;
557 gu = -0.391 * uvcos + -0.813 * uvsin;
558 gv = -0.391 * uvsin + -0.813 * uvcos;
559 bu = 2.018 * uvcos + 0 * uvsin;
560 bv = 2.018 * uvsin + 0 * uvcos;
561 rc = (-16 * ry + (-128) * ru + (-128) * rv) / 255.0 + brightness;
562 gc = (-16 * gy + (-128) * gu + (-128) * gv) / 255.0 + brightness;
563 bc = (-16 * by + (-128) * bu + (-128) * bv) / 255.0 + brightness;
564 rgamma = 1.0 / rgamma;
565 ggamma = 1.0 / ggamma;
566 bgamma = 1.0 / bgamma;
567 snprintf(yuv_prog, 1000, prog_template, ry, gy, by, rc, gc, bc, ru, gu, bu,
568 rv, gv, bv, rgamma, bgamma, bgamma);
569 ProgramString(GL_FRAGMENT_PROGRAM, GL_PROGRAM_FORMAT_ASCII,
570 strlen(yuv_prog), yuv_prog);
571 glGetIntegerv(GL_PROGRAM_ERROR_POSITION, &i);
572 if (i != -1)
573 mp_msg(MSGT_VO, MSGL_ERR,
574 "[gl] Error compiling fragment program, make sure your card supports\n"
575 "GL_ARB_fragment_program (use glxinfo to check).%.10s\n", &yuv_prog[i]);
576 }
577
578 /**
579 * \brief little helper function to create a lookup table for gamma
580 * \param map buffer to create map into
581 * \param size size of buffer
582 * \param gamma gamma value
583 */
584 static void gen_gamma_map(unsigned char *map, int size, float gamma) {
585 int i;
586 gamma = 1.0 / gamma;
587 for (i = 0; i < size; i++) {
588 float tmp = (float)i / (size - 1.0);
589 tmp = pow(tmp, gamma);
590 if (tmp > 1.0) tmp = 1.0;
591 if (tmp < 0.0) tmp = 0.0;
592 map[i] = 255 * tmp;
593 }
594 }
595
596 //! resolution of texture for gamma lookup table
597 #define LOOKUP_RES 512
598
599 /**
600 * \brief setup YUV->RGB conversion
601 * \param brightness brightness adjustment offset
602 * \param contrast contrast adjustment factor
603 * \param hue hue adjustment angle
604 * \param saturation saturation adjustment factor
605 * \param rgamma gamma value for red channel
606 * \param ggamma gamma value for green channel
607 * \param bgamma gamma value for blue channel
608 * \param type YUV conversion type
609 */
610 void glSetupYUVConversion(int type, float brightness, float contrast,
611 float hue, float saturation,
612 float rgamma, float ggamma, float bgamma) {
613 float uvcos = saturation * cos(hue);
614 float uvsin = saturation * sin(hue);
615 switch (type) {
616 case YUV_CONVERSION_COMBINERS:
617 glSetupYUVCombiners(uvcos, uvsin);
618 break;
619 case YUV_CONVERSION_FRAGMENT_LOOKUP:
620 {
621 unsigned char lookup_data[4 * LOOKUP_RES];
622 gen_gamma_map(lookup_data, LOOKUP_RES, rgamma);
623 gen_gamma_map(&lookup_data[LOOKUP_RES], LOOKUP_RES, ggamma);
624 gen_gamma_map(&lookup_data[2 * LOOKUP_RES], LOOKUP_RES, bgamma);
625 ActiveTexture(GL_TEXTURE3);
626 glCreateClearTex(GL_TEXTURE_2D, GL_LUMINANCE8, GL_LINEAR,
627 LOOKUP_RES, 4, 0);
628 glUploadTex(GL_TEXTURE_2D, GL_LUMINANCE, GL_UNSIGNED_BYTE, lookup_data,
629 LOOKUP_RES, 0, 0, LOOKUP_RES, 4, 0);
630 ActiveTexture(GL_TEXTURE0);
631 }
632 case YUV_CONVERSION_FRAGMENT:
633 case YUV_CONVERSION_FRAGMENT_POW:
634 glSetupYUVFragprog(brightness, contrast, uvcos, uvsin,
635 rgamma, ggamma, bgamma, type);
636 break;
637 }
638 }
639
640 /**
641 * \brief enable the specified YUV conversion
642 * \param target texture target for Y, U and V textures (e.g. GL_TEXTURE_2D)
643 * \param type type of YUV conversion
644 */
645 void inline glEnableYUVConversion(GLenum target, int type) {
646 if (type <= 0) return;
647 ActiveTexture(GL_TEXTURE1);
648 glEnable(target);
649 ActiveTexture(GL_TEXTURE2);
650 glEnable(target);
651 switch (type) {
652 case YUV_CONVERSION_COMBINERS:
653 glEnable(GL_REGISTER_COMBINERS_NV);
654 break;
655 case YUV_CONVERSION_FRAGMENT_LOOKUP:
656 ActiveTexture(GL_TEXTURE3);
657 glEnable(GL_TEXTURE_2D);
658 case YUV_CONVERSION_FRAGMENT_POW:
659 case YUV_CONVERSION_FRAGMENT:
660 glEnable(GL_FRAGMENT_PROGRAM);
661 break;
662 }
663 ActiveTexture(GL_TEXTURE0);
664 }
665
666 /**
667 * \brief disable the specified YUV conversion
668 * \param target texture target for Y, U and V textures (e.g. GL_TEXTURE_2D)
669 * \param type type of YUV conversion
670 */
671 void inline glDisableYUVConversion(GLenum target, int type) {
672 if (type <= 0) return;
673 ActiveTexture(GL_TEXTURE1);
674 glDisable(target);
675 ActiveTexture(GL_TEXTURE2);
676 glDisable(target);
677 switch (type) {
678 case YUV_CONVERSION_COMBINERS:
679 glDisable(GL_REGISTER_COMBINERS_NV);
680 break;
681 case YUV_CONVERSION_FRAGMENT_LOOKUP:
682 ActiveTexture(GL_TEXTURE3);
683 glDisable(GL_TEXTURE_2D);
684 case YUV_CONVERSION_FRAGMENT_POW:
685 case YUV_CONVERSION_FRAGMENT:
686 glDisable(GL_FRAGMENT_PROGRAM);
687 break;
688 }
689 ActiveTexture(GL_TEXTURE0);
690 }
691
692 /**
377 * \brief draw a texture part at given 2D coordinates 693 * \brief draw a texture part at given 2D coordinates
378 * \param x screen top coordinate 694 * \param x screen top coordinate
379 * \param y screen left coordinate 695 * \param y screen left coordinate
380 * \param w screen width coordinate 696 * \param w screen width coordinate
381 * \param h screen height coordinate 697 * \param h screen height coordinate
384 * \param tw texture part width in pixels 700 * \param tw texture part width in pixels
385 * \param th texture part height in pixels 701 * \param th texture part height in pixels
386 * \param sx width of texture in pixels 702 * \param sx width of texture in pixels
387 * \param sy height of texture in pixels 703 * \param sy height of texture in pixels
388 * \param rect_tex whether this texture uses texture_rectangle extension 704 * \param rect_tex whether this texture uses texture_rectangle extension
705 * \param is_yv12 if set, also draw the textures from units 1 and 2
389 */ 706 */
390 void glDrawTex(GLfloat x, GLfloat y, GLfloat w, GLfloat h, 707 void glDrawTex(GLfloat x, GLfloat y, GLfloat w, GLfloat h,
391 GLfloat tx, GLfloat ty, GLfloat tw, GLfloat th, 708 GLfloat tx, GLfloat ty, GLfloat tw, GLfloat th,
392 int sx, int sy, int rect_tex) { 709 int sx, int sy, int rect_tex, int is_yv12) {
710 GLfloat tx2 = tx / 2, ty2 = ty / 2, tw2 = tw / 2, th2 = th / 2;
393 if (!rect_tex) { 711 if (!rect_tex) {
394 tx /= sx; ty /= sy; tw /= sx; th /= sy; 712 tx /= sx; ty /= sy; tw /= sx; th /= sy;
713 tx2 = tx, ty2 = ty, tw2 = tw, th2 = th;
395 } 714 }
396 glBegin(GL_QUADS); 715 glBegin(GL_QUADS);
397 glTexCoord2f(tx, ty); 716 glTexCoord2f(tx, ty);
717 if (is_yv12) {
718 MultiTexCoord2f(GL_TEXTURE1, tx2, ty2);
719 MultiTexCoord2f(GL_TEXTURE2, tx2, ty2);
720 }
398 glVertex2f(x, y); 721 glVertex2f(x, y);
399 glTexCoord2f(tx, ty + th); 722 glTexCoord2f(tx, ty + th);
723 if (is_yv12) {
724 MultiTexCoord2f(GL_TEXTURE1, tx2, ty2 + th2);
725 MultiTexCoord2f(GL_TEXTURE2, tx2, ty2 + th2);
726 }
400 glVertex2f(x, y + h); 727 glVertex2f(x, y + h);
401 glTexCoord2f(tx + tw, ty + th); 728 glTexCoord2f(tx + tw, ty + th);
729 if (is_yv12) {
730 MultiTexCoord2f(GL_TEXTURE1, tx2 + tw2, ty2 + th2);
731 MultiTexCoord2f(GL_TEXTURE2, tx2 + tw2, ty2 + th2);
732 }
402 glVertex2f(x + w, y + h); 733 glVertex2f(x + w, y + h);
403 glTexCoord2f(tx + tw, ty); 734 glTexCoord2f(tx + tw, ty);
735 if (is_yv12) {
736 MultiTexCoord2f(GL_TEXTURE1, tx2 + tw2, ty2);
737 MultiTexCoord2f(GL_TEXTURE2, tx2 + tw2, ty2);
738 }
404 glVertex2f(x + w, y); 739 glVertex2f(x + w, y);
405 glEnd(); 740 glEnd();
406 } 741 }
407 742
408 #ifdef GL_WIN32 743 #ifdef GL_WIN32