/* ** 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 #include #include #include #include #include #include #include #include #include "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(); }