Reading Bitmap File and Draw it
A. First Edition
This is actually a practice for me to really understand 256color bitmap file and it is also a part of my searching for
color index in OpenGL. Amazingly enough I cannot find a way to create "color table" or palette. Of course there are many
examples about creating palette with MS window GDI. But surely it is not what I expect. Finally I have to draw it with
"drawPixels" with RGBA mode. This is a test and it is ugly. I really hesitate to post it in my archive like "jpeg-reader"
which is simply a "compiling project", pseudo-dll experiment on IJG source code. Anyway keeping the real face of history
is part of my job.
B.The problem
In 256 color bitmap there are a color table or palette and bitmap image which is actually byte-index of color table.
1. The file offset for "image-data" in BMPFILEHEADER(not exact name) is calculated from beginning of file. MSDN's
explanation mis-guide me. Either his English writing skill or my English reading skill is poor (or both).
2. You must be careful to skip the "padded" garbage at each scan line. (This is clearly stated in documents and I simply
don't get it at first. And I strongly believe many people make same repeat mistake like me. We are hopeless human.) In
plain English, it means the "width" of pixel is not equal to "width of image" because it is padded with "0" to make it
multiple times of 4. When reading, use paddedWidth=(pixelWidth+3)/4*4;
3. Also there several irrelative issues here. First I find my OpenGL is not uptodate. I try to find the newest gl.h and
cannot find in Google. Shame. Second, color table is not covered by many tutorials in web probably because they don't
understand it at all. Third, "glColorTable" is treated as "extension" of OpenGL. And I link with "glew32.dll", still I
get access error. Strange.
The idea is truly intuitive. And as I explained above, this is purely for temporary as my memory is very poor these days.
D.The major functions
.
E.Further improvement
F.File listing
1. bmpReader.cpp
2. fileTool.cpp(utility tool)
กก
file name: bmpReader.cpp
#include <windows.h> #include <stdio.h> #include <wingdi.h.> //#include <GL/glew.h> #include <GL/glut.h> //#include "extsetup.h" #pragma comment(lib, "glut32.lib") #pragma comment(lib, "opengl32.lib") //#pragma comment(lib, "glew32.lib") char* fileName="input.bmp"; //const int ScreenWidth=800; //const int ScreenHeight=600; //typedef unsigned char myRGBQUAD[4]; struct BMP { BITMAPINFO bitmapInfo; RGBQUAD* palette; unsigned char * bitmapImage; }; BMP bmp; bool loadBitmapFile256(char* fileName, BMP *bmp) { FILE *filePtr; //our file pointer bool stop=true; int i; int temp; BITMAPFILEHEADER bitmapFileHeader; //our bitmap file header //RGBQUAD* result; //int imageIdx=0; //image index counter //unsigned char tempRGB; //our swap variable //open filename in read binary mode if ((filePtr = fopen(fileName,"rb"))==NULL) { return false; } //read the bitmap file header fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1,filePtr); //verify that this is a bmp file by check bitmap id if (bitmapFileHeader.bfType !=0x4D42) { fclose(filePtr); return false; } //read the bitmap info header fread(&(bmp->bitmapInfo.bmiHeader), sizeof(BITMAPINFOHEADER),1,filePtr); //move file point to the begging of bitmap data //fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET); //printf("bits offset is %d\n", bitmapFileHeader.bfOffBits); //allocate enough memory for the bitmap image data //bitmapImage = new unsigned char[pBitmapInfo->bmiHeader.biSizeImage]; switch (bmp->bitmapInfo.bmiHeader.biBitCount) { case 0: printf("rgb\n"); break; case 1: printf("monochrome\n"); break; case 4: printf("16 color\n"); break; case 8: printf("256 color\n"); printf("height=%d, width=%d\n", bmp->bitmapInfo.bmiHeader.biHeight, bmp->bitmapInfo.bmiHeader.biWidth); //pBitmapInfo->bmiColors=new //RGBQUAD[pBitmapInfo->bmiHeader.biWidth*pBitmapInfo->bmiHeader.biHeight]; printf("color used %d\n", bmp->bitmapInfo.bmiHeader.biClrUsed); printf("image size %d\n", bmp->bitmapInfo.bmiHeader.biSizeImage); stop=false; if (bmp->bitmapInfo.bmiHeader.biClrUsed==0) { bmp->bitmapInfo.bmiHeader.biClrUsed=256; } break; case 16: printf("16bit \n"); break; case 24: printf("24bit\n"); break; case 32: printf("32bit\n"); break; } if (stop) { return false; } stop=true; switch (bmp->bitmapInfo.bmiHeader.biCompression) { case BI_RGB: printf("BI_RGB\n"); stop=false; break; case BI_RLE8: printf("BI_RLE8\n"); break; case BI_RLE4: printf("BI_RLE4\n"); break; case BI_BITFIELDS: printf("BI_BITFIELDS\n"); break; //case BI_JPEG: // printf("BI_JPEG\n"); // break; } if (stop) { return false; } unsigned char ch; bmp->palette=new RGBQUAD[bmp->bitmapInfo.bmiHeader.biClrUsed]; for (i=0; i<256; i++) { if ((temp=fread(bmp->palette+i, sizeof(RGBQUAD), 1, filePtr))!=1) { printf("what is going on read %d and size %d\n", temp, bmp->bitmapInfo.bmiHeader.biClrUsed*sizeof(RGBQUAD)); } /* ch=bmp->palette[i].rgbBlue; bmp->palette[i].rgbBlue=bmp->palette[i].rgbRed; bmp->palette[i].rgbRed=ch; bmp->palette[i].rgbReserved=255; //bmp->palette[i].rgbGreen+=50; */ //bmp->palette[i].rgbBlue=255-bmp->palette[i].rgbBlue; //bmp->palette[i].rgbGreen=255-bmp->palette[i].rgbGreen; //bmp->palette[i].rgbRed=255-bmp->palette[i].rgbRed; //bmp->palette[i].rgbReserved=255; } //fread(bmp->palette, sizeof(RGBQUAD), bmp->bitmapInfo.bmiHeader.biClrUsed, filePtr); bmp->bitmapImage=new unsigned char[bmp->bitmapInfo.bmiHeader.biSizeImage]; //printf("%d\n", sizeof(BITMAPFILEHEADER)); fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET); if ((temp=fread(bmp->bitmapImage, 1, bmp->bitmapInfo.bmiHeader.biSizeImage, filePtr)) !=bmp->bitmapInfo.bmiHeader.biSizeImage) { printf("what is going on read %d and image is %d?\n", temp, bmp->bitmapInfo.bmiHeader.biSizeImage); } ch=160; RGBQUAD myColor={64, 128, 192, 255}; for ( i=0; i<256; i++) { printf("[%d]:blue %d, green %d, red %d\n", i, bmp->palette[i].rgbBlue, bmp->palette[i].rgbGreen, bmp->palette[i].rgbRed); if (memcmp(&myColor, bmp->palette+i, sizeof(RGBQUAD))==0) { printf("\n*****************************************************\n"); printf("find it\n"); printf("\n*****************************************************\n"); } } fclose(filePtr); return true; } void init() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, bmp.bitmapInfo.bmiHeader.biWidth, 0, bmp.bitmapInfo.bmiHeader.biHeight, 0, 100); /* if (!SetupExtensionsFromString("GL_ARB_imaging")) { printf("setup extension failed\n"); exit(1); } */ glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); //glColorTable(GL_COLOR_TABLE, GL_RGBA, 256, GL_RGBA, GL_BYTE, bmp.palette); } void display() { int paddedWidth; int index, width=bmp.bitmapInfo.bmiHeader.biWidth, height=bmp.bitmapInfo.bmiHeader.biHeight; glClearColor(255,255,255,255); glClear(GL_COLOR_BUFFER_BIT); //glEnableClientState(GL_COLOR_ARRAY); //glColorPointer(4, GL_BYTE, 0, bmp.palette); //glEnableClientState(GL_INDEX_ARRAY); //glIndexPointer(GL_UNSIGNED_BYTE, 0, bmp.bitmapImage); glRasterPos2f(0,0); paddedWidth=(bmp.bitmapInfo.bmiHeader.biWidth+3)/4*4; //glPixelStorei(GL_UNPACK_LSB_FIRST, GL_TRUE); //glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE); for (int r=0; r<height; r++) { for (int c=0; c<width; c++) { glRasterPos2f(c,r); index=bmp.bitmapImage[r*paddedWidth+c]; //printf("[%d,%d] %d\n", r, c, index); glDrawPixels(1, 1, GL_BGRA, GL_UNSIGNED_BYTE, &bmp.palette[index]); } } //glDrawPixels(bmp.bitmapInfo.bmiHeader.biWidth, bmp.bitmapInfo.bmiHeader.biHeight, // GL_COLOR_INDEX, GL_BYTE, bmp.bitmapImage); //glDrawElements(GL_POINTS, bmp.bitmapInfo.bmiHeader.biWidth*bmp.bitmapInfo.bmiHeader.biHeight, // GL_UNSIGNED_BYTE, bmp.bitmapImage); //glDrawPixels(bmp.bitmapInfo.bmiHeader.biWidth, bmp.bitmapInfo.bmiHeader.biHeight, // GL_RGB, GL_BYTE, bmp.bitmapImage); glutSwapBuffers(); } void keyboard(unsigned char key, int x, int y) { switch (key) { case 27: exit(0); } glutPostRedisplay(); } int main(int argc, char** argv) { glutInit(&argc, argv); if (!loadBitmapFile256(fileName, &bmp)) { return 0; } glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_ALPHA); glutInitWindowPosition(0,0); glutInitWindowSize(bmp.bitmapInfo.bmiHeader.biWidth, bmp.bitmapInfo.bmiHeader.biHeight); glutCreateWindow("my test"); glutDisplayFunc(display); glutKeyboardFunc(keyboard); init(); glutMainLoop(); return 0; }
กก
file name: fileTool.cpp
#define XMD_H 1 #define IMPORTJPGDLL 1 #include <windows.h> #include <stdio.h> #include <vector> #include <jpeglib.h> #pragma comment(lib, "cjpeg.lib") #pragma comment(lib, "djpeg.lib") using namespace std; const int MY_MAX_PATH=MAX_PATH*4; const int MaxFileCount=500; char rootDir[MY_MAX_PATH]; char workDir[MY_MAX_PATH]; int rootDirLength=0; int directoryCount=0; int fileCount; int recursionLevel=0; vector<string> fileNameVector; typedef void (*CallbackFunc)(char* dir, char* fileName); void copyUpperLevel(char* dir, char* fileName) { char oldFileName[MY_MAX_PATH]; char newFileName[MY_MAX_PATH]; sprintf(oldFileName, "%s\\%s\\%s", rootDir, dir, fileName); sprintf(newFileName, "%s\\%s", workDir, fileName); if (!MoveFile(oldFileName, newFileName)) { printf("move %s to %s failed of %d\n", oldFileName, newFileName, GetLastError()); } } void convertJPG(char* dir, char* fileName) { char oldFileName[MY_MAX_PATH]; char newFileName[MY_MAX_PATH]; char* ptr; if ((ptr=strrchr(fileName, '.'))!=NULL) { if (_stricmp(ptr, ".jpg")!=0 && _stricmp(ptr, ".bmp")==0) { sprintf(oldFileName, "%s\\%s\\%s", rootDir, dir, fileName); sprintf(newFileName, "%s\\%s\\", rootDir, dir); strncat(newFileName, fileName, ptr-fileName); strcat(newFileName, ".jpg"); compressJPG(oldFileName, newFileName, 85); DeleteFile(oldFileName); } } } void CollapseAllSubTrees(char* dir, CallbackFunc callback, int level) { HANDLE handle; char curFileName[MY_MAX_PATH]; char wildFileName[MY_MAX_PATH]; WIN32_FIND_DATA ffd; sprintf(wildFileName, "%s\\*.*", dir); handle=FindFirstFile(wildFileName, &ffd); if (handle==INVALID_HANDLE_VALUE) { printf("findfirst failed of %s error code =%d\n", dir, GetLastError()); exit(19); } while (FindNextFile(handle, &ffd)) { if (strcmp(ffd.cFileName, "..")!=0 && strcmp(ffd.cFileName, ".")!=0) { sprintf(curFileName, "%s\\%s", dir, ffd.cFileName); if ((GetFileAttributes(curFileName)&FILE_ATTRIBUTE_DIRECTORY)) { CollapseAllSubTrees(curFileName, callback, level+1); } else { if (level>0) { callback(dir, ffd.cFileName); } } } } FindClose(handle); } void GenericRetrieveAllFile(char* dir, CallbackFunc callback, int stopLevel) { HANDLE handle; char curFileName[MY_MAX_PATH]; char wildFileName[MY_MAX_PATH]; WIN32_FIND_DATA ffd; if (recursionLevel>stopLevel) { return; } sprintf(wildFileName, "%s\\*.*", dir); handle=FindFirstFile(wildFileName, &ffd); if (handle==INVALID_HANDLE_VALUE) { printf("findfirst failed of %s error code =%d\n", dir, GetLastError()); exit(19); } while (FindNextFile(handle, &ffd)) { if (strcmp(ffd.cFileName, "..")!=0 && strcmp(ffd.cFileName, ".")!=0) { sprintf(curFileName, "%s\\%s", dir, ffd.cFileName); if ((GetFileAttributes(curFileName)&FILE_ATTRIBUTE_DIRECTORY)) { recursionLevel++; GenericRetrieveAllFile(curFileName, callback, stopLevel); recursionLevel--; } else { callback(dir, ffd.cFileName); } } } FindClose(handle); } void groupFile(char* dir, char* fileName) { char oldFileName[MY_MAX_PATH]; int oldDirLength, newDirLength; if (fileCount<MaxFileCount) { //sprintf(oldFileName, "%s\\%s", dir, fileName); fileNameVector.push_back(string(fileName)); fileCount++; } else { sprintf(oldFileName, "%s\\%s\\", rootDir, dir); oldDirLength=strlen(oldFileName); sprintf(rootDir+rootDirLength, "\\%s\\%s%d\\", dir, "pretty women", directoryCount); newDirLength=strlen(rootDir); if (!CreateDirectory(rootDir, NULL)) { printf("error of create directory %d\n", GetLastError()); } else { for (int i=0; i<fileNameVector.size(); i++) { strcpy(oldFileName+oldDirLength, fileNameVector[i].c_str()); strcpy(rootDir+newDirLength, fileNameVector[i].c_str()); if (!MoveFile(oldFileName, rootDir)) { printf("move %s to %s failed of %d\n", oldFileName, rootDir, GetLastError()); } } } rootDir[rootDirLength]='\0'; fileNameVector.clear(); fileCount=0; directoryCount++; } } void groupFiles(char* dir) { char buffer[MAX_PATH*4]; GetCurrentDirectory(MAX_PATH*4, buffer); sprintf(rootDir, "\\\\?\\%s", buffer); sprintf(workDir, "\\\\?\\%s\\%s", buffer, dir); rootDirLength=strlen(rootDir); recursionLevel=0; GenericRetrieveAllFile(dir, convertJPG, 6); //GenericRetrieveAllFile(dir, groupFile, 1); //CollapseAllSubTrees(dir, copyUpperLevel, 6); } int main(int argc, char** argv) { if (argc==2) { groupFiles(argv[1]); } return 0; }
กก