[BACK]Return to packedpix.c CVS log [TXT][DIR] Up to [Development] / projects / ogl-sample / main / gfx / samples / samples

File: [Development] / projects / ogl-sample / main / gfx / samples / samples / packedpix.c (download)

Revision 1.1.1.1 (vendor branch), Wed Jan 26 10:31:10 2000 UTC (17 years, 9 months ago) by ljp
Branch: SGI, MAIN
CVS Tags: tested_with_xf86_3_3, oglsi1_2_1, HEAD
Changes since 1.1: +0 -0 lines

Imported from P4

/*
** packedpix.c: Uses packed pixel types for DrawPixels, ReadPixels,
**              TexImage2D, and GetTexImage.
**
** Use -h option to display a list of command line options. Use Key h 
** in the created window for key options.
*/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include <GL/glut.h>
#include <GL/rgb.h>

/*----------------------------------------------------------------------*/

#define NUM_IMAGES 4
char *rgbFile = NULL;

GLboolean doubleBuffer = GL_FALSE;
GLboolean queryExts = GL_TRUE;

int winWidth = 300;
int winHeight = 300;

RGBImageRec *fileImage;

GLboolean isUseDisplayLists= GL_FALSE;

/*----------------------------------------------------------------------*/

/*
** This code is used to create an image of specified type and format
** for use with DrawPixels and TexImage.  Instead of writing
** code for any possible source image, a fixed type/format image is
** generated and then used to produce the requested type/format images.
** The fixed image is float/rgba.
*/

typedef struct _ConvertImageRec {
    GLfloat *fltImage;		/* original float RGBA image */
    void *newImage;             /* converted image */
    GLint imageWidth;		/* number of active pixels in a row */
    GLint imageHeight;		/* number of pixels in a column */
    GLint imageDataWidth;	/* original number of pixels in a row */
    GLint imageDataHeight;	/* original number of pixels in a column */
    GLenum type;		/* type of converted image */
    GLenum format;		/* format of converted image */
    GLint imageSize;		/* number of bytes in the converted image */
    int components;		/* number of components per pixel */
    int bytesPerComp;		/* number of bytes per component */
} ConvertImageRec;

/*
** Creates the float RGBA image from the unsigned byte RGB one that typically
** comes from a file. 
*/
static void CreateFloatImage(RGBImageRec *imageIn, ConvertImageRec *imageOut)
{
    GLubyte *rawData;
    GLint x, y;
    GLfloat *fltImage, *ptr;
    GLfloat alpha;
    GLint imageWidth, imageHeight;
    int imageSize;

    imageWidth = imageIn->sizeX;
    imageHeight = imageIn->sizeY;
    rawData = imageIn->data;

    imageSize = imageWidth * imageHeight * sizeof(GLfloat) * 4;
    fltImage = (float *)malloc(imageSize);
    if (NULL == fltImage) {
	fprintf(stderr, "Out of memory!\n");
	exit(1);
    }

    ptr = fltImage;
    for (y = 0; y < imageHeight; y++) {
        for (x = 0; x < imageWidth; x++) {
            *ptr++ = *rawData++ / 255.0;
	    /* make an alpha value by copying the green channel */
            alpha = *ptr++ = *rawData++ / 255.0; 
            *ptr++ = *rawData++ / 255.0;
            *ptr++ = alpha;
        }
    }
    imageOut->fltImage = fltImage;
    imageOut->imageWidth = imageWidth;
    imageOut->imageHeight = imageHeight;
    imageOut->imageDataWidth = imageOut->imageWidth;
    imageOut->imageDataHeight = imageOut->imageHeight;
}

/*
** Converts the float RGBA image pointed to by imageData->fltImage
** into an image of imageData->type and imageData->format pointed
** to by imageData->newImage.
*/
static void ConvertImage(ConvertImageRec *imageData)
{
    GLint imageWidth = imageData->imageDataWidth;
    GLint imageHeight = imageData->imageDataHeight;
    GLenum type = imageData->type;
    GLenum format = imageData->format;
    void *newImage;
    GLfloat *myImage = imageData->fltImage;
    int i, j, k;
    int imageSize;
    int components = imageData->components;

    /* Free the old image if there is one. */
    if (NULL != imageData->newImage) {
	free(imageData->newImage);
    }

    imageSize = imageWidth*imageHeight * components*imageData->bytesPerComp;

    newImage = (void *)malloc(imageSize);
    if (NULL == newImage) {
	fprintf(stderr, "Out of memory!\n");
	exit(1);
    }

    switch (type) {
	case GL_BITMAP:
	case GL_UNSIGNED_BYTE:
	case GL_BYTE:
	case GL_UNSIGNED_SHORT:
	case GL_SHORT:
	case GL_INT:
	case GL_UNSIGNED_INT:
	    break;

	case GL_FLOAT:
	    {
		GLfloat *data = newImage;

		for (i = 0; i < imageSize/4; i++) {
		    *data++ = *myImage++;
		}
	    }
	    break;

	case GL_UNSIGNED_BYTE_3_3_2_EXT:
	    {
		GLubyte *data = newImage;
		GLfloat *invalue;

		for (i = 0; i < imageHeight; i++) {
		    for (j = 0; j < imageWidth; j++) {

			/* first bitfield */
			invalue = myImage++;
			*data = ((GLubyte)(*invalue * 7) << 5) & 0xE0;

			/* second bitfield */
			invalue = myImage++;
			*data |= ((GLubyte)(*invalue * 7) << 2) & 0x1C;

			/* third bitfield */
			invalue = myImage++;
			*data |= (GLubyte)(*invalue * 3) & 0x03;

			data++;
			myImage += 1; /* skip the final component */
		    }
		}
	    }
	    break;

	case GL_UNSIGNED_SHORT_4_4_4_4_EXT:
	    {
		GLushort *data = newImage;
		GLfloat *invalue;

		for (i = 0; i < imageHeight; i++) {
		    for (j = 0; j < imageWidth; j++) {

			/* first bitfield */
			invalue = myImage++;
			*data = ((GLushort)(*invalue * 15) << 12) & 0xF000;

			/* second bitfield */
			invalue = myImage++;
			*data |= ((GLushort)(*invalue * 15) << 8) & 0x0F00;

			/* third bitfield */
			invalue = myImage++;
			*data |= ((GLushort)(*invalue * 15) << 4) & 0x00F0;

			/* fourth bitfield */
			invalue = myImage++;
			*data |= (GLushort)(*invalue * 15) & 0x000F;

			data++;
		    }
		}
	    }
	    break;

	case GL_UNSIGNED_SHORT_5_5_5_1_EXT:
	    {
		GLushort *data = newImage;
		GLfloat *invalue;

		for (i = 0; i < imageHeight; i++) {
		    for (j = 0; j < imageWidth; j++) {

			/* first bitfield */
			invalue = myImage++;
			*data = ((GLushort)(*invalue * 31) << 11) & 0xF800;

			/* second bitfield */
			invalue = myImage++;
			*data |= ((GLushort)(*invalue * 31) << 6) & 0x07C0;

			/* third bitfield */
			invalue = myImage++;
			*data |= ((GLushort)(*invalue * 31) << 1) & 0x003E;

			/* fourth bitfield */
			invalue = myImage++;
			*data |= (GLushort)(*invalue) & 0x0001;

			data++;
		    }
		}
	    }
	    break;

	case GL_UNSIGNED_INT_8_8_8_8_EXT:
	    {
		GLuint *data = newImage;
		GLfloat *invalue;

		for (i = 0; i < imageHeight; i++) {
		    for (j = 0; j < imageWidth; j++) {

			/* first bitfield */
			invalue = myImage++;
			*data = ((GLuint)(*invalue * 255) << 24) & 0xFF000000;

			/* second bitfield */
			invalue = myImage++;
			*data |= ((GLushort)(*invalue * 255) << 16) &
				 0x00FF0000;

			/* third bitfield */
			invalue = myImage++;
			*data |= ((GLuint)(*invalue * 255) << 8) & 0x0000FF00;

			/* fourth bitfield */
			invalue = myImage++;
			*data |= (GLuint)(*invalue * 255) & 0x000000FF;

			data++;
		    }
		}
	    }
	    break;

	case GL_UNSIGNED_INT_10_10_10_2_EXT:
	    {
		GLuint *data = newImage;
		GLfloat *invalue;

		for (i = 0; i < imageHeight; i++) {
		    for (j = 0; j < imageWidth; j++) {

			/* first bitfield */
			invalue = myImage++;
			*data = ((GLuint)(*invalue * 1023) << 22) & 0xFFC00000;

			/* second bitfield */
			invalue = myImage++;
			*data |= ((GLuint)(*invalue * 1023) << 12) & 0x003FF000;

			/* third bitfield */
			invalue = myImage++;
			*data |= ((GLuint)(*invalue * 1023) << 2) & 0x00000FFC;

			/* fourth bitfield */
			invalue = myImage++;
			*data |= (GLuint)(*invalue * 3) & 0x00000003;

			data++;
		    }
		}
	    }
	    break;
    } 
    imageData->newImage = newImage;
    imageData->imageSize = imageSize;
}
/*----------------------------------------------------------------------*/

ConvertImageRec imageData;

static int numTypes = 6;
static GLint components = 4;

void *tempbuf;

GLuint texSizeX, texSizeY;

/*----------------------------------------------------------------------*/

typedef struct cellStruct {
	int numRows;
	int numCols;
	int cellSizeX;
	int cellSizeY;
	int row;
	int col;
	int x;
	int y;
} cellStruct;

static cellStruct cell = {4,6, 0,0, 0,0, 0,0};

static void updateXY(cellStruct *cell)
{
    cell->x = cell->col * cell->cellSizeX;
    cell->y = cell->row * cell->cellSizeY;
}

static void setCell(cellStruct *cell, int col, int row)
{
    cell->col = col; cell->row = row;
    updateXY(cell);
}

static void nextCell(cellStruct *cell)
{
    cell->col ++;
    if (cell->col == cell->numCols) {
	cell->col = 0;
	cell->row ++;
	if (cell->row == cell->numRows) {
	    cell->row = 0;
	}
    }
    updateXY(cell);
}

/*----------------------------------------------------------------------*/

/*
** Calculates the largest power of two that's less than the value.
*/
static unsigned int maxTexSize(unsigned int value)
{
    int power;

    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &power);
    if (value < power) {
	while(!(power & value)) power >>= 1;
    }
    return power;
}

/*
** Makes a ramped unsigned byte RGB image.
*/
static RGBImageRec *InitRampedImage(void)
{
    int i,j;
    int sizeX = 128;
    int sizeY = 128;
    int imageSize = sizeX*sizeY*3;
    GLubyte *byteImage, *pImg;
    RGBImageRec *rgbImage;

    rgbImage = (RGBImageRec *)malloc(sizeof(RGBImageRec));
    byteImage = (GLubyte *)malloc(imageSize);
    pImg = byteImage;

    for (i=0; i < sizeX; i++) {
	for (j=0; j < sizeY; j++) {
	    *pImg++ = 255 * (float)i/sizeX;
	    *pImg++ = 255 * (1-(float)j/sizeY);
	    *pImg++ = 255 * (1-(float)i/sizeX);
	}
    }
    rgbImage->data = byteImage;
    rgbImage->sizeX = sizeX;
    rgbImage->sizeY = sizeY;
    return rgbImage;
}

static void Init(void)
{
    int i;
    GLfloat quant4[4];

    /*
    ** Create a float RGBA image from the file data.
    */
    CreateFloatImage(fileImage, &imageData);
    free(fileImage->data);
    free(fileImage);
    fileImage = NULL;

    /*
    ** Now create a working image with the float image.
    */
    imageData.type = GL_FLOAT;
    imageData.format = GL_RGBA;
    imageData.newImage = NULL;
    imageData.components = components = 4;
    imageData.bytesPerComp = 4;
    ConvertImage(&imageData);

    /*
    ** Allocate a buffer to read images into.
    */
    tempbuf = malloc(imageData.imageSize+3);
    if (NULL == tempbuf) {
	fprintf(stderr, "Out of memory!\n");
	exit(1);
    }

    /*
    ** Find the largest texture size that will work with this image.
    */
    texSizeX = maxTexSize((unsigned int) imageData.imageWidth);
    texSizeY = maxTexSize((unsigned int) imageData.imageHeight);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, imageData.imageDataWidth);
    glPixelStorei(GL_PACK_ROW_LENGTH, imageData.imageDataWidth);

    glScissor(0,0,winWidth,winHeight);
    glEnable(GL_SCISSOR_TEST);
    glColor3f(1.0, 1.0, 1.0);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

}


/*----------------------------------------------------------------------*/

/*
** Allows the caller to run through a list of types,
** setting the format, components, and bytesPerComp accordingly.
*/
static void nextType(ConvertImageRec *imageData)
{
    /*
    ** Note that packed pixel types are designed so that
    ** the image size can be computed by making the number
    ** of components = 1 and the bytesPerComponent = bytes
    ** per group.  This allows the arithmatic to be performed
    ** uniformly without introducing floating point values.
    ** imageData->components is used to compute the image size.
    ** components is passed to TexImage.
    */
    switch(imageData->type) {
      case GL_FLOAT:
	imageData->type = GL_UNSIGNED_BYTE_3_3_2_EXT;
	imageData->format = GL_RGB;
	imageData->components = 1;
	imageData->bytesPerComp = 1;
	components = 3;
	break;
      case GL_UNSIGNED_BYTE_3_3_2_EXT:
	imageData->type = GL_UNSIGNED_SHORT_4_4_4_4_EXT;
	imageData->format = GL_RGBA;
	imageData->components = 1;
	imageData->bytesPerComp = 2;
	components = 4;
	break;
      case GL_UNSIGNED_SHORT_4_4_4_4_EXT:
	imageData->type = GL_UNSIGNED_SHORT_5_5_5_1_EXT;
	imageData->format = GL_RGBA;
	imageData->components = 1;
	imageData->bytesPerComp = 2;
	components = 4;
	break;
      case GL_UNSIGNED_SHORT_5_5_5_1_EXT:
	imageData->type = GL_UNSIGNED_INT_8_8_8_8_EXT;
	imageData->format = GL_RGBA;
	imageData->components = 1;
	imageData->bytesPerComp = 4;
	components = 4;
	break;
      case GL_UNSIGNED_INT_8_8_8_8_EXT:
	imageData->type = GL_UNSIGNED_INT_10_10_10_2_EXT;
	imageData->format = GL_RGBA;
	imageData->components = 1;
	imageData->bytesPerComp = 4;
	components = 4;
	break;
      case GL_UNSIGNED_INT_10_10_10_2_EXT:
	imageData->type = GL_FLOAT;
	imageData->format = GL_RGBA;
	imageData->components = 4;
	imageData->bytesPerComp = 4;
	components = 4;
	break;
    }
}

/*----------------------------------------------------------------------*/

static void Reshape(int width, int height)
{
    winWidth = width; winHeight = height;
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, width, 0, height, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glScissor(0,0,width,height);

    /* Update the cell sizes to match the new window size. */
    cell.cellSizeX = width/cell.numCols;
    cell.cellSizeY = height/cell.numRows;
    imageData.imageWidth = cell.cellSizeX-4;
    imageData.imageHeight = cell.cellSizeY-4;

    /* Make sure we don't go stomping off the edges of our image data. */
    if (imageData.imageWidth > imageData.imageDataWidth) {
	imageData.imageWidth = imageData.imageDataWidth;
    }
    if (imageData.imageHeight > imageData.imageDataHeight) {
	imageData.imageHeight = imageData.imageDataHeight;
    }
}


/*
** Runs the packed pixel types through DrawPixels/ReadPixels 
** and TexImage/GetTexImage.
*/
static void Draw(void)
{
    int i;

    glClear(GL_COLOR_BUFFER_BIT);

    setCell(&cell, 0,0);

    /* DrawPixels */

    for (i=0; i < numTypes; i++) {
	glRasterPos3f(0,0,-1);
	glBitmap(0,0,0,0, cell.x, cell.y, NULL);
	ConvertImage(&imageData);

	if (isUseDisplayLists) {
	   glNewList(1,GL_COMPILE);
	   assert(glGetError() == GL_NO_ERROR);
	   glDrawPixels(imageData.imageWidth, imageData.imageHeight, 
			imageData.format, imageData.type, imageData.newImage);
	   assert(glGetError() == GL_NO_ERROR);
	   glEndList();
	   assert(glGetError() == GL_NO_ERROR);
	   glCallList(1);
	   assert(glGetError() == GL_NO_ERROR);
	   
	}
	else {	
	   glDrawPixels(imageData.imageWidth, imageData.imageHeight, 
			imageData.format, imageData.type, imageData.newImage);
        }

	nextType(&imageData);
	nextCell(&cell);
    }

    /* ReadPixels */

    for (i=0; i < numTypes; i++) {
	glReadPixels(0,0, imageData.imageWidth, imageData.imageHeight, 
		imageData.format, imageData.type, tempbuf);
	glRasterPos3f(0,0,-1);
	glBitmap(0,0,0,0, cell.x, cell.y, NULL);
	glDrawPixels(imageData.imageWidth, imageData.imageHeight, 
		imageData.format, imageData.type, tempbuf);

	nextType(&imageData);
	nextCell(&cell);
    }

    /* TexImage */

    glEnable(GL_TEXTURE_2D);

    for (i=0; i < numTypes; i++) {
	ConvertImage(&imageData);

	if (isUseDisplayLists) {
	   glNewList(2,GL_COMPILE);
	   assert(glGetError() == GL_NO_ERROR);
	   glTexImage2D(GL_TEXTURE_2D, 0, components, texSizeX, texSizeY,
			0, imageData.format, imageData.type, 
			imageData.newImage);

	   assert(glGetError() == GL_NO_ERROR);	
	   glEndList();
	   assert(glGetError() == GL_NO_ERROR);

	   glCallList(2);
	}
	else {
	   glTexImage2D(GL_TEXTURE_2D, 0, components, texSizeX, texSizeY,
			0, imageData.format, imageData.type, 
			imageData.newImage);
	}

	glBegin(GL_QUADS);
	    glTexCoord2f(0.0, 0.0);
	    glVertex2i(cell.x, cell.y);
	    glTexCoord2f(1.0, 0.0);
	    glVertex2i(cell.x+imageData.imageWidth-1, cell.y);
	    glTexCoord2f(1.0, 1.0);
	    glVertex2i(cell.x+imageData.imageWidth-1, 
			cell.y+imageData.imageHeight-1);
	    glTexCoord2f(0.0, 1.0);
	    glVertex2i(cell.x, cell.y+imageData.imageHeight-1);
	glEnd();

	nextType(&imageData);
	nextCell(&cell);
    }

    /* GetTexImage */

    for (i=0; i < numTypes; i++) {
	glTexImage2D(GL_TEXTURE_2D, 0, 4, texSizeX, texSizeY,
			0, GL_RGBA, GL_FLOAT, imageData.fltImage);
	glGetTexImage(GL_TEXTURE_2D, 0, imageData.format, 
			imageData.type, tempbuf);
	glTexImage2D(GL_TEXTURE_2D, 0, components, texSizeX, texSizeY,
			0, imageData.format, imageData.type, tempbuf);
	glBegin(GL_QUADS);
	    glTexCoord2f(0.0, 0.0);
	    glVertex2i(cell.x, cell.y);
	    glTexCoord2f(1.0, 0.0);
	    glVertex2i(cell.x+imageData.imageWidth-1, cell.y);
	    glTexCoord2f(1.0, 1.0);
	    glVertex2i(cell.x+imageData.imageWidth-1, 
			cell.y+imageData.imageHeight-1);
	    glTexCoord2f(0.0, 1.0);
	    glVertex2i(cell.x, cell.y+imageData.imageHeight-1);
	glEnd();

	nextType(&imageData);
	nextCell(&cell);
    }
    glDisable(GL_TEXTURE_2D);

    if (doubleBuffer) {
	glutSwapBuffers();
    } else {
	glFlush();
    }
}

/*----------------------------------------------------------------------*/

static char *keyList[][2] = {
    {"R", "Reset draw routine"},
    {"d", "toggle use of display lists"},
    {"",""},
    {"h", "print this list"},
    {"ESC", "exit"},
    {NULL, NULL}
};

static void Key(unsigned char key, int x, int y)
{
    int i;

    switch (key) {
      case 'h':
	for (i = 0; keyList[i][0] != NULL; i++) {
	    printf("%-20s %s\n", keyList[i][0], keyList[i][1]);
	}
	break;
      case 'd':
	if (isUseDisplayLists) {
	   printf("Not using display lists.\n");
	   isUseDisplayLists= GL_FALSE;
	}
	else {
	   printf("Using display lists in bottom row & 2nd row from top.\n");
	   isUseDisplayLists= GL_TRUE;
	}
	glutPostRedisplay();
	break;
      case 'R':
	glutPostRedisplay();
	break;

      case 27:
	exit(0);
    }
}

/*----------------------------------------------------------------------*/

static char *argList[][2] = {
    {"-sb", "single buffered"},
    {"-db", "double buffered"},
    {"-f filename", "filename for texture"},
    {"-nq", "skip querying extensions"},
    {"-h", "print this list"},
    {"Press H in app window to get key interpretations", ""},
    {NULL, NULL}
};

static void Args(int argc, char **argv)
{
    GLint i;

    for (i = 1; i < argc; i++) {
	if (strcmp(argv[i], "-sb") == 0) {
	    doubleBuffer = GL_FALSE;
	} else if (strcmp(argv[i], "-db") == 0) {
	    doubleBuffer = GL_TRUE;
	} else if (strcmp(argv[i], "-f") == 0) {
	    if (i+1 >= argc || argv[i+1][0] == '-') {
		printf("-f (No file name).\n");
		exit(1);
	    } else {
		rgbFile = argv[++i];
	    }
	} else if (strcmp(argv[i], "-nq") == 0) {
	    queryExts = GL_FALSE;
	} else if (strcmp(argv[i], "-h") == 0) {
	    for (i = 0; argList[i][0] != NULL; i++) {
		printf("%-30s %s\n", argList[i][0], argList[i][1]);
	    }
	    exit(0);
	}
    }
}

/*----------------------------------------------------------------------*/

static GLboolean QueryExtension(char *extName)
{
    /*
    ** Search for extName in the extensions string. Use of strstr()
    ** is not sufficient because extension names can be prefixes of
    ** other extension names. Could use strtok() but the constant
    ** string returned by glGetString can be in read-only memory.
    */
    char *p;
    char *end;
    int extNameLen;

    extNameLen = strlen(extName);

    p = (char *)glGetString(GL_EXTENSIONS);
    if (NULL == p) {
	return GL_FALSE;
    }

    end = p + strlen(p);

    while (p < end) {
	int n = strcspn(p, " ");
	if ((extNameLen == n) && (strncmp(extName, p, n) == 0)) {
	    return GL_TRUE;
	}
	p += (n + 1);
    }
    return GL_FALSE;
}

/*----------------------------------------------------------------------*/

int main(int argc, char **argv)
{
    GLenum type;

    glutInit(&argc, argv);
    Args(argc, argv);

    /*
    ** The image is loaded before the window is opened to
    ** allow sizing the window based on the image size.
    */
    if (NULL == rgbFile) {
        fileImage = InitRampedImage();
    }
    else {
	fileImage = rgbImageLoad(rgbFile);
	if (NULL == fileImage) {
	    fprintf(stdout, "Warning: file %s not found.\n", rgbFile);
	    fileImage = InitRampedImage();
	}
    }
    cell.cellSizeX = fileImage->sizeX + 4;
    cell.cellSizeY = fileImage->sizeY + 4;
    winWidth = cell.numCols*cell.cellSizeX - 4;
    winHeight = cell.numRows*cell.cellSizeY - 4;

    type = GLUT_RGB;
    type |= (doubleBuffer) ? GLUT_DOUBLE : GLUT_SINGLE;
    glutInitDisplayMode(type);
    glutInitWindowSize(winWidth, winHeight);
    glutCreateWindow("Packed Pixel Test");

    if (queryExts && !QueryExtension("GL_EXT_packed_pixels")) {
	fprintf(stdout, "Warning: packed pixel extension not found.\n");
	exit(0);
    }

    Init();

    glutReshapeFunc(Reshape);
    glutKeyboardFunc(Key);
    glutDisplayFunc(Draw);
    glutMainLoop();
}