(此节内容对应NEHE教程第33课)
利用压缩算法可以减低图片大小。降低图片存储所需的物理存储空间,但是也会相应的增加图片解压缩带来的时间消耗
对于TGA文件的压缩可以简单理解为,在图片信息存储区。并不是把每一个像素点的RGB值都进行存储,而是临近的像素点如果RGB值相同,那么进行合并形成“块”对于一个”块“来说 他们有一个共同的RGB值,但是块是由很多个像素点组成的
0~~11 | 头信息 | 第3位(2)是2 代表未压缩 第3位(2)是10 代表未压缩RLE压缩 |
12~13 | 图像宽度 | byte[13]*256+byte[12] |
14~15 | 图像高度 | byte[15]*256+byte[14] |
16 | 图像每像素存储占用位(bit)数 | 24或32 |
接下来读一个字符 代表接下来的”块“中包含的像素点数 用chumk代替
如果 0<=chumk<=127 代表接下来的 chumk+1个像素点的RGB值都不同,因此每一个都需要读取
如果 128<=chumk 代表接下来的 chumk-127个像素点的RGB值相同,因此只需要读取一次即可
Texture.h
#ifndef __TEXTURE_H__ #define __TEXTURE_H__ #pragma comment(lib, "Opengl32.lib") #include <windows.h> typedef struct { GLubyte * imageData; GLuint bpp; GLuint width; GLuint height; GLuint texID; GLuint type; } Texture; #endif
Tga.h
#ifndef __TGA_H__ #define __TGA_H__ #pragma comment(lib, "Opengl32.lib") #include <windows.h> #include "texture.h" typedef struct { GLubyte Header[12]; } TGAHeader; typedef struct { GLubyte header[6]; GLuint bytesPerPixel; GLuint imageSize; GLuint temp; GLuint type; GLuint Height; GLuint Width; GLuint Bpp; } TGA; TGAHeader tgaheader; TGA tga; GLubyte uTGAcompare[12] = {0,0,2, 0,0,0,0,0,0,0,0,0}; GLubyte cTGAcompare[12] = {0,0,10,0,0,0,0,0,0,0,0,0}; bool LoadUncompressedTGA(Texture *, char *, FILE *); bool LoadCompressedTGA(Texture *, char *, FILE *); #endif
TGALoader.cpp
#include "tga.h" bool LoadTGA(Texture * texture, char * filename) { FILE * fTGA; fTGA = fopen(filename, "rb"); if(fTGA == NULL) { MessageBox(NULL, "Could not open texture file", "ERROR", MB_OK); return false; } if(fread(&tgaheader, sizeof(TGAHeader), 1, fTGA) == 0) { MessageBox(NULL, "Could not read file header", "ERROR", MB_OK); e if(fTGA != NULL) { fclose(fTGA); } return false; } if(memcmp(uTGAcompare, &tgaheader, sizeof(tgaheader)) == 0) { LoadUncompressedTGA(texture, filename, fTGA); } else if(memcmp(cTGAcompare, &tgaheader, sizeof(tgaheader)) == 0) { LoadCompressedTGA(texture, filename, fTGA); } else { MessageBox(NULL, "TGA file be type 2 or type 10 ", "Invalid Image", MB_OK); fclose(fTGA); return false; } return true; } bool LoadUncompressedTGA(Texture * texture, char * filename, FILE * fTGA) { if(fread(tga.header, sizeof(tga.header), 1, fTGA) == 0) { MessageBox(NULL, "Could not read info header", "ERROR", MB_OK); if(fTGA != NULL) { fclose(fTGA); } return false; } texture->width = tga.header[1] * 256 + tga.header[0]; texture->height = tga.header[3] * 256 + tga.header[2]; texture->bpp = tga.header[4]; tga.Width = texture->width; tga.Height = texture->height; tga.Bpp = texture->bpp; if((texture->width <= 0) || (texture->height <= 0) || ((texture->bpp != 24) && (texture->bpp !=32))) { MessageBox(NULL, "Invalid texture information", "ERROR", MB_OK); if(fTGA != NULL) { fclose(fTGA); } return false; } if(texture->bpp == 24) texture->type = GL_RGB; else texture->type = GL_RGBA; tga.bytesPerPixel = (tga.Bpp / 8); tga.imageSize = (tga.bytesPerPixel * tga.Width * tga.Height); texture->imageData = (GLubyte *)malloc(tga.imageSize); if(texture->imageData == NULL) { MessageBox(NULL, "Could not allocate memory for image", "ERROR", MB_OK); fclose(fTGA); return false; } if(fread(texture->imageData, 1, tga.imageSize, fTGA) != tga.imageSize) { MessageBox(NULL, "Could not read image data", "ERROR", MB_OK); if(texture->imageData != NULL) { free(texture->imageData); } fclose(fTGA); return false; } // Byte Swapping Optimized By Steve Thomas for(GLuint cswap = 0; cswap < (int)tga.imageSize; cswap += tga.bytesPerPixel) { texture->imageData[cswap] ^= texture->imageData[cswap+2] ^= texture->imageData[cswap] ^= texture->imageData[cswap+2]; } fclose(fTGA); return true; } bool LoadCompressedTGA(Texture * texture, char * filename, FILE * fTGA) { if(fread(tga.header, sizeof(tga.header), 1, fTGA) == 0) { MessageBox(NULL, "Could not read info header", "ERROR", MB_OK); if(fTGA != NULL) { fclose(fTGA); } return false; } texture->width = tga.header[1] * 256 + tga.header[0]; texture->height = tga.header[3] * 256 + tga.header[2]; texture->bpp = tga.header[4]; tga.Width = texture->width; tga.Height = texture->height; tga.Bpp = texture->bpp; if((texture->width <= 0) || (texture->height <= 0) || ((texture->bpp != 24) && (texture->bpp !=32))) { MessageBox(NULL, "Invalid texture information", "ERROR", MB_OK); if(fTGA != NULL) { fclose(fTGA); } return false; } if(texture->bpp == 24) texture->type = GL_RGB; else texture->type = GL_RGBA; tga.bytesPerPixel = (tga.Bpp / 8); tga.imageSize = (tga.bytesPerPixel * tga.Width * tga.Height); texture->imageData = (GLubyte *)malloc(tga.imageSize); if(texture->imageData == NULL) MessageBox(NULL, "Could not allocate memory for image", "ERROR", MB_OK); fclose(fTGA); return false; } GLuint pixelcount = tga.Height * tga.Width; GLuint currentpixel = 0; GLuint currentbyte = 0; GLubyte * colorbuffer = (GLubyte *)malloc(tga.bytesPerPixel); do { GLubyte chunkheader = 0; if(fread(&chunkheader, sizeof(GLubyte), 1, fTGA) == 0) { MessageBox(NULL, "Could not read RLE header", "ERROR", MB_OK); if(fTGA != NULL) { fclose(fTGA); } if(texture->imageData != NULL) { free(texture->imageData); } return false; } if(chunkheader < 128) { chunkheader++; for(short counter = 0; counter < chunkheader; counter++) { if(fread(colorbuffer, 1, tga.bytesPerPixel, fTGA) != tga.bytesPerPixel) { MessageBox(NULL, "Could not read image data", "ERROR", MB_OK); if(fTGA != NULL) { fclose(fTGA); } if(colorbuffer != NULL) { free(colorbuffer); } if(texture->imageData != NULL) { free(texture->imageData); } return false; } texture->imageData[currentbyte ] = colorbuffer[2]; texture->imageData[currentbyte + 1 ] = colorbuffer[1]; texture->imageData[currentbyte + 2 ] = colorbuffer[0]; if(tga.bytesPerPixel == 4) { texture->imageData[currentbyte + 3] = colorbuffer[3]; } currentbyte += tga.bytesPerPixel; currentpixel++; if(currentpixel > pixelcount) { MessageBox(NULL, "Too many pixels read", "ERROR", NULL); if(fTGA != NULL) { fclose(fTGA); } if(colorbuffer != NULL) { free(colorbuffer); } if(texture->imageData != NULL) { free(texture->imageData); } return false; } } } else { chunkheader -= 127; if(fread(colorbuffer, 1, tga.bytesPerPixel, fTGA) != tga.bytesPerPixel) { MessageBox(NULL, "Could not read from file", "ERROR", MB_OK); if(fTGA != NULL) { fclose(fTGA); } if(colorbuffer != NULL) { free(colorbuffer); } if(texture->imageData != NULL) { free(texture->imageData); } return false; } for(short counter = 0; counter < chunkheader; counter++) { texture->imageData[currentbyte ] = colorbuffer[2]; texture->imageData[currentbyte + 1 ] = colorbuffer[1]; texture->imageData[currentbyte + 2 ] = colorbuffer[0]; if(tga.bytesPerPixel == 4) { texture->imageData[currentbyte + 3] = colorbuffer[3]; } currentbyte += tga.bytesPerPixel; currentpixel++; if(currentpixel > pixelcount) { MessageBox(NULL, "Too many pixels read", "ERROR", NULL); if(fTGA != NULL) { fclose(fTGA); } if(colorbuffer != NULL) { free(colorbuffer); } if(texture->imageData != NULL) { free(texture->imageData); } return false; } } } } while(currentpixel < pixelcount); fclose(fTGA); return true; }
#include "header.h" #include "texture.h" bool LoadTGA(Texture *, char *); float spin; Texture texture[2]; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int LoadGLTextures() { int Status=FALSE; // Load The Bitmap, Check For Errors. if (LoadTGA(&texture[0], "Data/Uncompressed.tga") && LoadTGA(&texture[1], "Data/Compressed.tga")) { Status=TRUE; for (int loop=0; loop<2; loop++) { glGenTextures(1, &texture[loop].texID); glBindTexture(GL_TEXTURE_2D, texture[loop].texID); glTexImage2D(GL_TEXTURE_2D, 0, texture[loop].bpp / 8, texture[loop].width, texture[loop].height, 0, texture[loop].type, GL_UNSIGNED_BYTE, texture[loop].imageData); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); if (texture[loop].imageData) { free(texture[loop].imageData); } } } return Status; } void ReSizeGLScene(GLsizei width, GLsizei height) { if (height==0) { height=1; } glViewport(0,0,width,height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Calculate The Aspect Ratio Of The Window gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } int InitGL(void) { if (!LoadGLTextures()) { return FALSE; } glEnable(GL_TEXTURE_2D); glShadeModel(GL_SMOOTH); glClearColor(0.0f, 0.0f, 0.0f, 0.5f); glClearDepth(1.0f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); return TRUE; } void DrawGLScene(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0f,0.0f,-10.0f); spin+=0.05f; for (int loop=0; loop<20; loop++) { glPushMatrix(); glRotatef(spin+loop*18.0f,1.0f,0.0f,0.0f); glTranslatef(-2.0f,2.0f,0.0f); glBindTexture(GL_TEXTURE_2D, texture[0].texID); glBegin(GL_QUADS); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); glEnd(); glPopMatrix(); glPushMatrix(); glTranslatef(2.0f,0.0f,0.0f); glRotatef(spin+loop*36.0f,0.0f,1.0f,0.0f); glTranslatef(1.0f,0.0f,0.0f); glBindTexture(GL_TEXTURE_2D, texture[1].texID); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 0.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 0.0f); glEnd(); glPopMatrix(); } glFlush(); } void rotate() { spin+=0.05f; glutPostRedisplay(); } void keyboard(unsigned char key,int x,int y) { switch (key) { case 'S': glutIdleFunc(rotate); break; case 'R': glutIdleFunc(NULL); break; } } int main(int argc,char **argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH); glutInitWindowSize(800,600); glutInitWindowPosition(100,100); glutCreateWindow("加载压缩TGA"); InitGL(); glutDisplayFunc(DrawGLScene); glutKeyboardFunc(keyboard); glutReshapeFunc(ReSizeGLScene); glutMainLoop(); }