/*
* MODIFIED by Bruce Merry (bmerry@iafrica.com) on 10/11/2000
* - fixed to handle arbitrary tile dimensions, not just 1x2
* - max_texture_size renamed to texture_size and used as a variable
* (to change the max texture size, edit the assigned value in the decl)
* - hardcoded 256's changed to texture_size to allow small texture sizes
* (e.g. for very low-res movies)
* - all pieces of movie copied into the top left corner of texture tiles,
* instead of being offset
* - mechanism for keeping tiles aligned changed: a one texel border is
* included in the tiles, which I think is used by the filtering even though
* it is not explicitly selected for rendering (I think - I don't know much
* about OpenGL, I've just fiddled until it looked right)
* - removed glmovie_is_power_of_2: it was not needed and
* it only went up to 2048 anyway.
*/
#include "glmovie.h"
#include <malloc.h>
#include <string.h>
/* Some data is redundant at this stage. */
typedef struct glmovie_texture_t {
GLuint id; /* OpenGL texture id. */
GLuint poly_width; /* Quad width for tile. */
GLuint poly_height; /* Quad height for tile. */
GLuint movie_width; /* Width of movie inside tile. */
GLuint movie_height; /* Height of movie inside tile. */
GLuint skip_rows; /* Number of rows of movie to skip */
GLuint skip_pixels; /* Number of columns of movie to skip */
GLuint row; /* Row number of tile in scheme. */
GLuint col; /* Column number of tile in scheme. */
} glmovie_texture;
/* Boy, is this not thread safe. */
/* Our evil maximum texture size. Boo 3Dfx! */
static GLuint texture_size = 256;
/* Keep this around for easy freeing later. */
static GLuint* texture_ids = NULL;
/* Our main data. */
static glmovie_texture* textures = NULL;
static GLuint num_texture_rows = 0;
static GLuint num_texture_cols = 0;
/* Width and height of all tiling. */
static GLuint tiled_width = 0;
static GLuint tiled_height = 0;
/* Width and height of entire movie. */
static GLuint movie_width = 0;
static GLuint movie_height = 0;
/*
* Draw the frame data.
*
* Parameters:
* frame: Actual RGBA frame data
*/
void glmovie_draw( GLubyte* frame )
{
GLuint i;
GLdouble shift;
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity( );
shift = 1 / ((double) texture_size);
for (i = 0; i < num_texture_rows * num_texture_cols; i++) {
glBindTexture( GL_TEXTURE_2D, textures[i].id );
glPixelStorei( GL_UNPACK_ROW_LENGTH, movie_width );
glPixelStorei( GL_UNPACK_SKIP_ROWS, textures[i].skip_rows );
glPixelStorei( GL_UNPACK_SKIP_PIXELS, textures[i].skip_pixels );
glTexSubImage2D( GL_TEXTURE_2D,
0,
0, /* offset_x */
0, /* offset_y */
textures[i].movie_width + 2,
textures[i].movie_height + 2,
GL_RGBA,
GL_UNSIGNED_BYTE,
frame );
glBegin( GL_QUADS );
glTexCoord2f( shift, shift );
glVertex2i( textures[i].col * texture_size,
textures[i].row * texture_size );
glTexCoord2f( shift, shift + (textures[i].movie_height)/((double) texture_size) );
glVertex2i( textures[i].col * texture_size,
(textures[i].row + 1) * texture_size);
glTexCoord2f( shift + (textures[i].movie_width)/((double) texture_size),
shift + (textures[i].movie_height)/((double) texture_size) );
glVertex2i( (textures[i].col + 1) * texture_size,
(textures[i].row + 1) * texture_size);
glTexCoord2f( shift + (textures[i].movie_width)/((double) texture_size), shift );
glVertex2i( (textures[i].col + 1) * texture_size,
textures[i].row * texture_size );
glEnd( );
}
}
/*
* Here we need to center the OpenGL viewport within the
* window size that we are given.
*
* Parameters:
* width: Width of the window in pixels
* height: Height of the window in pixels
*/
void glmovie_resize( GLuint width, GLuint height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity( );
gluOrtho2D( 0, tiled_width, tiled_height, 0 );
}
/*
* Calculates the next power of 2 given a particular value.
* Useful for calculating proper texture sizes for non power-of-2
* aligned texures.
*
* Parameters:
* seed: Value to begin from
* Returns:
* Next power of 2 beginning from 'seed'
*/
GLuint glmovie_next_power_of_2( GLuint seed )
{
GLuint i;
for( i = 1; i < seed; i *= 2 ) { };
return i;
}
/*
* Initialize the movie player subsystem with the width and height
* of the *movie data* (as opposed to the window).
*
* Parameters:
* width: Width of movie in pixels
* height: Height of movie in pixels
* Returns:
* GL_NO_ERROR on success
* Any of the enumerated GL errors on failure
*/
GLenum glmovie_init( GLuint width, GLuint height )
{
/* Initial black texels. */
GLubyte* pixels;
/* Absolute offsets from within tiled frame. */
GLuint offset_x = 0;
GLuint offset_y = 0;
GLuint skip_rows = 0;
GLuint skip_pixels = 0;
GLuint i, j, current;
/* Save original movie dimensions. */
movie_width = width;
movie_height = height;
/* Get the power of 2 dimensions. */
tiled_width = glmovie_next_power_of_2( width );
tiled_height = glmovie_next_power_of_2( height );
while ( texture_size > tiled_width || texture_size > tiled_height )
texture_size /= 2;
/* Now break it up into quads. */
num_texture_rows = tiled_height / texture_size;
num_texture_cols = tiled_width / texture_size;
/* Time for fun with data structures. */
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
glEnable( GL_TEXTURE_2D );
glEnable( GL_DITHER );
texture_ids = (GLuint*) malloc( sizeof( GLuint ) * num_texture_rows * num_texture_cols );
if( !texture_ids ) {
return GL_OUT_OF_MEMORY;
}
glGenTextures( num_texture_rows * num_texture_cols, texture_ids );
textures = (glmovie_texture*) malloc( sizeof( glmovie_texture ) *
num_texture_rows * num_texture_cols );
if( !textures ) {
glDeleteTextures( num_texture_rows * num_texture_cols, texture_ids );
free( texture_ids );
return GL_OUT_OF_MEMORY;
}
for ( i = 0; i < num_texture_rows; i++ ) {
skip_pixels = 0;
for ( j = 0; j < num_texture_cols; j++ ) {
current = i * num_texture_cols + j;
/* Setup texture. */
textures[current].id = texture_ids[current];
textures[current].poly_width = texture_size;
textures[current].poly_height = texture_size;
textures[current].movie_width =
(movie_width - 2) * (j + 1) / num_texture_cols - skip_pixels;
textures[current].movie_height =
(movie_height - 2) * (i + 1) / num_texture_rows - skip_rows;
textures[current].row = i;
textures[current].col = j;
textures[current].skip_pixels = skip_pixels;
textures[current].skip_rows = skip_rows;
skip_pixels += textures[current].movie_width;
pixels = (GLubyte*) malloc( textures[current].poly_width * textures[current].poly_height * 4 );
memset( pixels, 0, textures[current].poly_width * textures[current].poly_height * 4 );
if( !pixels ) {
glDeleteTextures( num_texture_rows * num_texture_cols, texture_ids );
free( texture_ids );
free( textures );
return GL_OUT_OF_MEMORY;
}
/* Do all of our useful binding. */
glBindTexture( GL_TEXTURE_2D, textures[current].id );
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
/* Specify our 256x256 black texture. */
glTexImage2D( GL_TEXTURE_2D,
0,
GL_RGB,
textures[current].poly_width,
textures[current].poly_height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
pixels );
free( pixels );
}
skip_rows += textures[current].movie_height;
}
/* Simple state setup at the end. */
glClearColor( 0.0, 0.0, 0.0, 0.0 );
return glGetError( );
}
/*
* Free any resources associated with the movie player.
*/
void glmovie_quit( void )
{
glDeleteTextures( num_texture_rows * num_texture_cols, texture_ids );
free( texture_ids );
free( textures );
}