Getting incorrect width when decoding bitmap - c++

I have an error with my source code which basically causes bitmap images to appear too wide. for example it will print the width and the height and the height is perfect (256) and the width should also be 256 but the programs says it is billions of pixels wide and it is different everytime. here is the source code.
#include "glob.h"
/* Image type - contains height, width, and data */
struct Image {
unsigned long sizeX;
unsigned long sizeY;
char *data;
};
typedef struct Image Image;
int ImageLoad(char *filename, Image *image) {
FILE *file;
unsigned long size; // size of the image in bytes.
unsigned long i; // standard counter.
unsigned short int planes; // number of planes in image (must be 1)
unsigned short int bpp; // number of bits per pixel (must be 24)
char temp; // temporary color storage for bgr-rgb conversion.
// make sure the file is there.
if ((file = fopen(filename, "rb"))==NULL){
printf("bitmap Not Found : %s\n",filename);
return 0;
}
// seek through the bmp header, up to the width/height:
fseek(file, 18, SEEK_CUR);
// read the width
if ((i = fread(&image->sizeX, 4, 1, file)) != 1) {
printf("Error reading width from %s.\n", filename);
return 0;
}
printf("Width of %s: %lu\n", filename, image->sizeX);
// read the height
if ((i = fread(&image->sizeY, 4, 1, file)) != 1) {
printf("Error reading height from %s.\n", filename);
return 0;
}
printf("Height of %s: %lu\n", filename, image->sizeY);
// calculate the size (assuming 24 bits or 3 bytes per pixel).
size = image->sizeX * image->sizeY * 3;
// read the planes
if ((fread(&planes, 2, 1, file)) != 1) {
printf("Error reading planes from %s.\n", filename);
return 0;
}
if (planes != 1) {
printf("Planes from %s is not 1: %u\n", filename, planes);
return 0;
}
// read the bpp
if ((i = fread(&bpp, 2, 1, file)) != 1) {
printf("Error reading bpp from %s.\n", filename);
return 0;
}
if (bpp != 24) {
printf("Bpp from %s is not 24: %u\n", filename, bpp);
return 0;
}
// seek past the rest of the bitmap header.
fseek(file, 24, SEEK_CUR);
// read the data.
image->data = (char *) malloc(size);
if (image->data == NULL) {
printf("Error allocating memory for color-corrected image data\n");
return 0;
}
if ((i = fread(image->data, size, 1, file)) != 1) {
printf("Error reading image data from %s.\n", filename);
return 0;
}
for (i=0; i<size; i+=3) { // reverse all of the colors. (bgr -> rgb)
temp = image->data[i];
image->data[i] = image->data[i+2];
image->data[i+2] = temp;
}
// we're done.
return 0;
}
// Load Bitmaps And Convert To Textures
void glob::LoadGLTextures() {
// Load Texture
Image *image1;
// allocate space for texture
image1 = (Image *) malloc(sizeof(Image));
if (image1 == NULL) {
printf("(image1 == NULL)\n");
exit(0);
}
ImageLoad("data/textures/NeHe.bmp", image1);
// Create Texture
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture); // 2d texture (x and y size)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // scale linearly when image bigger than texture
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // scale linearly when image smalled than texture
// 2d texture, level of detail 0 (normal), 3 components (red, green, blue), x size from image, y size from image,
// border 0 (normal), rgb color data, unsigned byte data, and finally the data itself.
glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->sizeX, image1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, image1->data);
};
glob.h is this:
#ifndef GLOB_H_INCLUDED
#define GLOB_H_INCLUDED
#include <iostream>
#include <stdlib.h>
#include <stdio.h> // Header file for standard file i/o.
#include <GL/glx.h> /* this includes the necessary X headers */
#include <GL/gl.h>
//#include <GL/glut.h> // Header File For The GLUT Library
//#include <GL/glu.h> // Header File For The GLu32 Library
#include <X11/X.h> /* X11 constant (e.g. TrueColor) */
#include <X11/keysym.h>
class glob {
bool Running;
GLuint texture; //make an array when we start using more then 1
Display *dpy;
Window win;
XEvent event;
GLboolean doubleBuffer;
GLboolean needRedraw;
GLfloat xAngle, yAngle, zAngle;
float camera_x, camera_y, camera_z;
public:
glob();
int OnExecute();
public:
int init(int argc, char **argv);
void LoadGLTextures();
void OnEvent();
void redraw(void);
};
#endif // GLOB_H_INCLUDED
can any body help me fix this problem?

Lots of things could be going wrong.
If it's a very old file, it could have a BITMAPCOREHEADER which has size fields that are only 2 bytes each.
Is your machine little endian? BMP files are stored little endian.
Note that height may be negative, (which implies it's a top-down bitmap instead of a bottom up one). If you interpret a small negative number as an unsigned 32-bit int, you'll see values in the billions.
Also, your seek to the actual pixel data assumes that it starts right after the bitmap header. This is common, but not required. The file header contains the offset of the actual pixel data. (Microsoft documentation calls this the "bitmap bits" or the "color data".)
I recommend doing a hex dump of the beginning of your file and step through it by hand to make sure all your offsets and assumptions are correct. Feel free to paste the beginning of a hex dump into your question.
Are you on Windows? Can you just call LoadImage?

Related

JPEG image rotation in C++ using libjpeg

I am trying to rotate a JPEG image in C++ using libjpeg v9 based on the "Orientation" parameter present in EXIF metadata. I am able to get the "Orientation" parameter and on its basis, i am also able to rotate image into another file so that rotated image corresponds to "Orientation" value 1.
See code, which i have taken from "jpegtran.c" file and working fine(reading EXIF metadata code is not present):
#include <iostream>
#include <jpeglib.h>
#include <jerror.h>
#include "transupp.h"
void setTransformation(jpeg_transform_info *transformObj, JXFORM_CODE transformation){
transformObj->perfect = FALSE;
transformObj->trim = FALSE;
transformObj->force_grayscale = FALSE;
transformObj->crop = FALSE;
transformObj->transform = transformation;
}
void releaseRes(j_decompress_ptr srcPtr, j_compress_ptr destPtr){
jpeg_finish_compress(destPtr);
jpeg_destroy_compress(destPtr);
(void) jpeg_finish_decompress(srcPtr);
jpeg_destroy_decompress(srcPtr);
}
void rotateImage(const char *inputFilename, const char *outputFilename, JXFORM_CODE transformVal){
FILE *inputFile = fopen(inputFilename, "r");
if(inputFile==NULL){
std::cerr<<"ERROR: cannot open input file\n";
return;
}
struct jpeg_decompress_struct srcObj;
struct jpeg_error_mgr srcErrMgr;
struct jpeg_compress_struct destObj;
struct jpeg_error_mgr destErrMgr;
jvirt_barray_ptr *srcCoefArr;
jvirt_barray_ptr *destCoefArr;
//transformation object
jpeg_transform_info transformObj;
//set error handler
srcObj.err = jpeg_std_error(&srcErrMgr);
jpeg_create_decompress(&srcObj);
destObj.err = jpeg_std_error(&destErrMgr);
jpeg_create_compress(&destObj);
//set the transformation properties
setTransformation(&transformObj, transformVal);
jpeg_stdio_src(&srcObj, inputFile);
JCOPY_OPTION copyOpt = JCOPYOPT_DEFAULT;
jcopy_markers_setup(&srcObj, copyOpt);
(void) jpeg_read_header(&srcObj, TRUE);
if(!jtransform_request_workspace(&srcObj, &transformObj)){
std::cerr<<"Transformation is not perfect\n";
return;
}
srcCoefArr = jpeg_read_coefficients(&srcObj);
jpeg_copy_critical_parameters(&srcObj, &destObj);
destCoefArr = jtransform_adjust_parameters(&srcObj, &destObj, srcCoefArr, &transformObj);
FILE *outputFile = fopen(outputFilename, "wb");
if(outputFile==NULL){
std::cerr<<"ERROR: cannot open output file\n";
fclose(inputFile);
releaseRes(&srcObj, &destObj);
return;
}
jpeg_stdio_dest(&destObj, outputFile);
jpeg_write_coefficients(&destObj, destCoefArr);
jcopy_markers_execute(&srcObj, &destObj, copyOpt);
jtransform_execute_transformation(&srcObj, &destObj, srcCoefArr, &transformObj);
releaseRes(&srcObj, &destObj);
//close files
fclose(inputFile);
fclose(outputFile);
}
However, i do not want to store rotated image into another file and rather want to rotate in place into buffer or using temp buffer but without compression as in above code.
Below is the code to get the decompressed data into buffer:
void rotateImage(const char *filename){
FILE *file = fopen(filename, "r");
if(!file){
std::cerr<<"Error in reading file\n";
return;
}
struct jpeg_decompress_struct info;
struct jpeg_error_mgr jerr;
info.err = jpeg_std_error(&jerr);
jpeg_CreateDecompress(&info, JPEG_LIB_VERSION, (size_t) sizeof(struct jpeg_decompress_struct));
jpeg_stdio_src(&info, file);
(void) jpeg_read_header(&info, TRUE);
jpeg_start_decompress(&info);
uint32_t channels = 3;
uint32_t rowStride = info.output_width * channels;
uint64_t dataSize = rowStride * info.output_height;
unsigned char *buffer = new unsigned char[dataSize];
unsigned char *rowData[1];
while(info.output_scanline < info.output_height){
//initial value of output_Scanline state var is 0
rowData[0] = buffer + info.output_scanline * rowStride;
jpeg_read_scanlines(&info, rowData, 1);
}
/*Now, i want to rotate this buffer (or with other temp buffer without compression as in
first code) as per "orientation", either 90, 180, 270*/
/* here */
jpeg_finish_decompress(&info);
jpeg_destroy_decompress(&info);
fclose(file);
delete buffer;
}
Though, i tried to rotate buffer using temp buffer (analogous to matrix rotation for non-square matrix) with following code for 90 degree:
//90 degree clockwise
unsigned char *tmpBuf = new unsigned char[dataSize];
int row = info.output_height;
int col = info.output_width;
for(int i=0; i<row; i+=1){
for(int j=0;j<col; j+=1){
//copied 3 bytes as each pixed takes 3 bytes for RGB
memcpy(tmpBuf + (j*row + row-i-1)*3, buffer + (i*col + j)*3, 3);
}
}
However, i believe, it is not correct way for rotating JPEG as the rotated data is not accepted by the application i am sending this data to(FYI, i am rotating it as per "Orientation" as application respect it). Which makes me believe that it is not the correct way to rotate JPEG image. As with first method, first rotating into compressed data and then decompressing again into buffer is accepted by the application i am sending data to. But, i think, it is not the better way to do it.
So, i need your help for this. Please let me know the step required to achieve it. Any code example or tutorials will also be helpful.
Thanks

I am unable to load a .BMP image

I am new to opengl. I am trying to load an image using opengl. But I am unable to do so. It gives me this error:
*** Error in `./lena': double free or corruption (top) : 0x0000000001353070 ***
I don't know what I am doing wrong. My code has been given below. Actually it is not my code. I have seen it in another post in Stack Overflow by someone named Ollo.
#include <bits/stdc++.h>
#include <GL/glut.h>
using namespace std;
struct BITMAPFILEHEADER
{
int bfType; //specifies the file type
long long bfSize; //specifies the size in bytes of the bitmap file
int bfReserved1; //reserved; must be 0
int bfReserved2; //reserved; must be 0
long long bOffBits; //species the offset in bytes from the bitmapfileheader to the bitmap bits
};
struct BITMAPINFOHEADER
{
long long biSize; //specifies the number of bytes required by the struct
long long biWidth; //specifies width in pixels
long long biHeight; //species height in pixels
int biPlanes; //specifies the number of color planes, must be 1
int biBitCount; //specifies the number of bit per pixel
long long biCompression;//spcifies the type of compression
long long biSizeImage; //size of image in bytes
long long biXPelsPerMeter; //number of pixels per meter in x axis
long long biYPelsPerMeter; //number of pixels per meter in y axis
long long biClrUsed; //number of colors used by th ebitmap
long long biClrImportant; //number of colors that are important
};
int main(void){
FILE *filePtr;
BITMAPFILEHEADER bitmapFileHeader;
BITMAPINFOHEADER *bitmapInfoHeader = new BITMAPINFOHEADER;
unsigned char *bitmapImage; //store image data
int imageIdx=0; //image index counter
unsigned char tempRGB; //our swap variable
filePtr = fopen("lena.bmp","rb");
if (filePtr == NULL)
cout << "ERROR!!! 1" << endl;
fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER),1,filePtr);
fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER),1,filePtr); // small edit. forgot to add the closing bracket at sizeof
//move file point to the begging of bitmap data
fseek(filePtr, bitmapFileHeader.bOffBits, SEEK_SET);
//allocate enough memory for the bitmap image data
bitmapImage = (unsigned char*)malloc(bitmapInfoHeader->biSizeImage);
//verify memory allocation
if (!bitmapImage)
{
free(bitmapImage);
fclose(filePtr);
}
//read in the bitmap image data
fread(bitmapImage, bitmapInfoHeader->biSizeImage, 1, filePtr);
//make sure bitmap image data was read
if (bitmapImage == NULL)
{
fclose(filePtr);
}
//swap the r and b values to get RGB (bitmap is BGR)
for (imageIdx = 0;imageIdx < bitmapInfoHeader->biSizeImage;imageIdx+=3)
{
tempRGB = bitmapImage[imageIdx];
bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
bitmapImage[imageIdx + 2] = tempRGB;
}
return 0;
}
This code that you got from another answer on StackOverflow has some awkward issues.
It checks to see if bitmapImage is 0 and if it is, it immediately calls free (...) on bitmapImage
It needs to return on failure in order to prevent crashing.
fread (...) does not change whether or not bitmapImage is NULL
Here are some minimum changes that you need to make:
if (filePtr == NULL) {
cout << "ERROR!!! 1" << endl;
return -1; // FAILURE, so return!
}
[...]
//allocate enough memory for the bitmap image data
bitmapImage = (unsigned char*)malloc(bitmapInfoHeader->biSizeImage);
//verify memory allocation
if (!bitmapImage)
{
//free(bitmapImage); // This does not belong here!
fclose(filePtr);
return -2; // FAILURE, so return!
}
//read in the bitmap image data
fread(bitmapImage, bitmapInfoHeader->biSizeImage, 1, filePtr);
// THIS IS POINTLESS TOO, fread (...) is not going to change the address of
// bitmapImage.
/*
//make sure bitmap image data was read
if (bitmapImage == NULL)
{
fclose(filePtr);
}
*/
//swap the r and b values to get RGB (bitmap is BGR)
for (imageIdx = 0;imageIdx < bitmapInfoHeader->biSizeImage;imageIdx+=3)
{
tempRGB = bitmapImage[imageIdx];
bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];
bitmapImage[imageIdx + 2] = tempRGB;
}
As mentioned in my comment, you should not be writing your own code to read in a .bmp file (unless it's for an assignment, or something like that). You say you're on mint linux, so you might try either FreeImage or ImageMagick. The code you're using doesn't handle index color images, for example, and probably a host of other things.
that's how I load a BMP file of 24 bits per pixel, not for indexed colour bitmaps, download winbgim from here: http://www.mediafire.com/file/z9wnl0c70tiacqn/winbgim_DevCpp.zip/file, is in dev-c++, you probably have to load a couple of parameters for the linker, the path is: project, project options, parameters, and write: -lbgi -lgdi32 -luser32 -lcomdlg32 -luuid -loleaut32 -lole32 -mwindows -lwinmm, one thing more, after installing winbgim, go to dev c and create new project as console graphics application, this item must be in the project list if winbgim is properly installed,
I wrote "write to shared memory", because I wrote the file also in memory although I did not access the file this way, but from the same file
#include <winbgim.h>
#include <string.h>
#include<windows.h>
#include<stdio.h>
#include <stdlib.h>
const char *Filename;
BITMAPFILEHEADER FileHeader;
BITMAPINFOHEADER InfoHeader;
int k;
typedef struct{
BYTE colorr;
BYTE colorg;
BYTE colorb;
}cine;
cine color;
HANDLE hArch, hProy;
LPSTR base,puntero;
DWORD tam;
int main()
{
int gdriver=9;
int gmode=2;
// initgraph(&gdriver,&gmode, "");
cleardevice();
UnmapViewOfFile(base);
CloseHandle(hProy);
CloseHandle(hArch);
char *p;
char *base;
DWORD buf;
Filename="D:\\music\\IMSLP00795-BWV0971\\01.bmp";
int gd = DETECT, gm;
int x = 320, y = 240, radius;
k=0;
int i;
int j;
FILE *File=NULL;
if(!Filename)
{
MessageBox(NULL,"Konnte Filename nicht finden!","Error",MB_OK|MB_ICONERROR);
}
else
{
File=fopen("D:\\music\\IMSLP00795-BWV0971\\01.bmp","rb");
}
fread(&FileHeader,sizeof(BITMAPFILEHEADER),1,File);
if(FileHeader.bfType != 0x4D42)
{
MessageBox(NULL,"Ungültiges Bildformat!","Error",MB_OK|MB_ICONERROR);
exit(1);
}
printf("tamaño total del archivo %d\n",FileHeader.bfSize);
printf("comienzo del mapa de bits (imagen en pixels) en bits %d\n",FileHeader.bfOffBits);
buf=FileHeader.bfOffBits/8; //offset from the begining of BMP file (pixel array)
printf("comienzo del mapa de bits en bytes desde el origen del archivo %d\n",buf);
fread(&InfoHeader,sizeof(BITMAPINFOHEADER),1,File);
printf("horizontal resolution in pixels por metro %li\n",InfoHeader.biWidth);
printf("vertical resolution in pixels por metro %li\n",InfoHeader.biHeight);
printf("numero de bits por pixel %d", InfoHeader.biBitCount);
initwindow(InfoHeader.biWidth,InfoHeader.biHeight);
hArch = CreateFile("D:\\music\\IMSLP00795-BWV0971\\01.bmp", /* file name */
GENERIC_ALL , /* read/write access */
0, /* no sharing of the file */
NULL, /* default security */
OPEN_ALWAYS, /* open new or existing file */
FILE_ATTRIBUTE_NORMAL, /* routine file attributes */
NULL); /* no file template */
if (hArch==INVALID_HANDLE_VALUE){
fprintf(stderr,"no puede abrirse el archivo");
}
hProy = CreateFileMapping(hArch, /* file handle */
NULL, /* default security */
PAGE_READWRITE, /* read/write access to mapped pages */
0, /* map entire file */
0,
TEXT("SharedObject")); /* named shared memory object */
/* write to shared memory */
base=(LPSTR)MapViewOfFile(hProy,FILE_MAP_ALL_ACCESS,0,0,0);
tam=GetFileSize(hArch,NULL);
int cont=0;
puntero=base;
p=base+FileHeader.bfOffBits;
k=0;int t=0,v,l;
fseek(File,FileHeader.bfOffBits,SEEK_SET );
int read=0,read2=0;
k=0;
for( i=0; i<InfoHeader.biWidth; i++ ) {
fread(&color,sizeof(cine),1,File);
read += sizeof(cine);
printf( "Pixel %d: %3d %3d %3d\n", i+1, int(color.colorb), int(color.colorg), int(color.colorr) );
}
if( read % 4 != 0 ) {
read2 = 4 - (read%4);
printf( "Padding: %d bytes\n", read2 );
//fread( &color, read2, 1, File );
}
fseek(File,FileHeader.bfOffBits,SEEK_SET );
for (i=0;i<InfoHeader.biHeight;i++)
for(j=0;j<InfoHeader.biWidth ;j++)
{
fread(&color,sizeof(cine),1,File);
putpixel(j,InfoHeader.biHeight- i,COLOR(int(color.colorb),int(color.colorg),int(color.colorr)));
if(j==InfoHeader.biWidth-1&&read2!=0)fseek(File,read2,SEEK_CUR);
}
fclose(File);
UnmapViewOfFile(base);
CloseHandle(hProy);
CloseHandle(hArch);
getch();
}

DDS texture transparency rendered black Opengl

I am currently trying to render textured objects in Opengl. Everything worked fine until I wanted to render a texture with transparency. Instead of showing the the object transparent it just rendered in total black.
The method fo loading the texture file is this:
// structures for reading and information variables
char magic[4];
unsigned char header[124];
unsigned int width, height, linearSize, mipMapCount, fourCC;
unsigned char* dataBuffer;
unsigned int bufferSize;
fstream file(path, ios::in|ios::binary);
// read magic and header
if (!file.read((char*)magic, sizeof(magic))){
cerr<< "File " << path << " not found!"<<endl;
return false;
}
if (magic[0]!='D' || magic[1]!='D' || magic[2]!='S' || magic[3]!=' '){
cerr<< "File does not comply with dds file format!"<<endl;
return false;
}
if (!file.read((char*)header, sizeof(header))){
cerr<< "Not able to read file information!"<<endl;
return false;
}
// derive information from header
height = *(int*)&(header[8]);
width = *(int*)&(header[12]);
linearSize = *(int*)&(header[16]);
mipMapCount = *(int*)&(header[24]);
fourCC = *(int*)&(header[80]);
// determine dataBuffer size
bufferSize = mipMapCount > 1 ? linearSize * 2 : linearSize;
dataBuffer = new unsigned char [bufferSize*2];
// read data and close file
if (file.read((char*)dataBuffer, bufferSize/1.5))
cout<<"Loading texture "<<path<<" successful"<<endl;
else{
cerr<<"Data of file "<<path<<" corrupted"<<endl;
return false;
}
file.close();
// check pixel format
unsigned int format;
switch(fourCC){
case FOURCC_DXT1:
format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
break;
case FOURCC_DXT3:
format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
break;
case FOURCC_DXT5:
format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
break;
default:
cerr << "Compression type not supported or corrupted!" << endl;
return false;
}
glGenTextures(1, &ID);
glBindTexture(GL_TEXTURE_2D, ID);
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
unsigned int offset = 0;
/* load the mipmaps */
for (unsigned int level = 0; level < mipMapCount && (width || height); ++level) {
unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize;
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height,
0, size, dataBuffer + offset);
offset += size;
width /= 2;
height /= 2;
}
textureType = DDS_TEXTURE;
return true;
In the fragment shader I just set the gl_FragColor = texture2D( myTextureSampler, UVcoords )
I hope that there is an easy explanation such as some code missing.
In the openGL initialization i glEnabled GL_Blend and set a blend function.
Does anyone have an idea of what I did wrong?
Make sure the blend function is the correct function for what you are trying to accomplish. For what you've described that should be glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
You probably shouldn't set the blend function in your openGL initialization function but should wrap it around your draw calls like:
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
//gl draw functions (glDrawArrays,glDrawElements,etc..)
glDisable(GL_BLEND)
Are you clearing the 2D texture binding before you swap buffers? i.e ...
glBindTexture(GL_TEXTURE_2D, 0);

Custom static library linking error with MinGW and QT Creator

I have made a static library Win32 (it doesn't actually contain Win32 code) library in MSVC 2008 and I am currently trying to link to it in QT Creator. But whenever I compile, I get the error:
C:\Users\Snowball\Documents\QT Creator\Libraries\FontSystem\main.cpp:-1: error: undefined reference to `NGUI::CFont::CFont()'
The library is a font system that loads a PNG using FreeImage and then "cuts" it up into individual symbols then passes the image data to gluBuild2DMipMaps() which then creates an OpenGL texture out of it to be used later on when drawing strings. I have ALL my class methods defined and the whole class is part of a namespace called NGUI. This way the font system won't be confused with another if for some reason two are in use. To link this library, I simply added the following code to my .pro file: LIBS += FontSystem.lib
and the only file in the application right now is this:
#include "fontsystem.h"
using namespace NGUI;
int main(int argc, char *argv[])
{
cout<< "Starting the FontSystem..."<< endl;
CFont *cFont = new CFont();
cout<< "FontSystem Started!"<< endl;
system("sleep 1");
return 0;
}
The file fontsystem.h looks like this:
#ifndef FONTSYSTEM_H
#define FONTSYSTEM_H
// Include the Basic C++ headers.
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <assert.h>
#include <limits>
using namespace std;
// Include the OpenGL and Image Headers.
#include <GL/gl.h>
#include <GL/glu.h>
#include "utilities.h"
// Macros.
#define DIM(x) (sizeof(x)/sizeof(*(x)))
/*
The Font System works by loading all the font images (max image size 32px^2) into memory and storing
the OpenGL texture ID's into an array that can be access at all times. The DrawString() functions will
search through the string for the specified character requested to draw and then it will draw a quad
and paint the texture on it. Since all the images are pre-loaded, no loading of the images at load time
is necessary, this is memory consuming but efficiant for the CPU. ALL functions WILL return a character
string specifing errors or success. A function will work as long as it can and when an error happens,
unless the error is fatal, the functions will NOT rollback changes! This ensures that during testing, a
very certain bug can be spotted.
*/
namespace NGUI // This namespace is used to ensure no confusion happens. This font system paints 2D fonts for GUIs.
{
class CFont
{
public:
CFont();
~CFont();
template<typename tChar> char* DrawString(tChar *apString, int aiSize, int aiX, int aiY);
template<typename tNum> char* DrawString(tNum anNumber, int aiSize, int aiX, int aiY);
private:
char* SetupFont(); // This function will load as many images as possible into memory.
GLuint miTextIDs[36];
int miDrawIDs[1024];
};
}
#endif // FONTSYSTEM_H
EDIT:
Here is the implementation file for fontsystem.h
#include "fontsystem.h"
#include "fontsystem.h"
namespace NGUI
{
CFont::CFont()
{
SetupFont();
}
CFont::~CFont() {}
template<typename tChar>
char* CFont::DrawString(tChar *apString, int aiSize, int aiX, int aiY)
{
// Search the string from most significant character to least significant.
int iSelectIndex = 0;
for(size_t i = 0; apString[i] != NULL; ++i)
{
iSelectIndex = apString[i] >= '0' && apString[i] <= '9' ? (apString[i] - '0') :
apString[i] >= 'A' && apString[i] <= 'Z' ? (apString[i] - 'A' + 10) :
apString[i] >= 'a' && apString[i] <= 'z' ? (apString[i] - 'a' + 10) :
apString[i] == ' ' ? 36 : // This is a special case, This see's if the current character is a space or not.
-1;
if(iSelectIndex == -1)
{
return "The String Is Corrupt! Aborting!";
}
// Add the current selected character to the drawing array.
miDrawIDs[i] = iSelectIndex;
}
// Go through and draw each and every character.
for(size_t i = 0; i < DIM(miDrawIDs); ++i)
{
// Paint each qaud with the X,Y coordinates. After each quad has been successfully drawn,
// Add the size to the X coordinate. NOTE: Each character is square!!!
if(miDrawIDs[i] != 36)
{
glBindTexture(GL_TEXTURE_2D, miDrawIDs[i]);
}
// The font color is always white.
glColor4f(1.0, 1.0, 1.0, 0.0); // The alpha argument in the function call is set to 0 to allow color only where image data is present.
glBegin(GL_QUADS);
glTexCoord2i(0, 0);
glVertex2i(aiX, aiY);
glTexCoord2i(1, 0);
glVertex2i(aiX + aiSize, aiY);
glTexCoord2i(1, 1);
glVertex2i(aiX + aiSize, aiY + aiSize);
glTexCoord2i(0, 1);
glVertex2i(aiX, aiY + aiSize);
glEnd();
// Now, increase the X position by the size.
aiX += aiSize;
}
return "Successful Drawing of String!";
}
template<typename tNum>
char* CFont::DrawString(tNum anNumber, int aiSize, int aiX, int aiY)
{
// Convert the supplied number to a character string via snprintf().
char *vTempString = new char[1024];
snprintf(vTempString, 1024, "%f", anNumber);
// Next, run DrawString().
return DrawString<char>(vTempString, aiSize, aiX, aiY);
}
char* CFont::SetupFont()
{
// First Load The PNG file holding the font.
FreeImage_Initialise(false);
FIBITMAP *spBitmap = FreeImage_Load(FIF_PNG, "Font.png", BMP_DEFAULT);
if(!spBitmap)
{
return "Was Unable To Open/Decode Bitmap!";
}
// Do an image sanity check.
if(!FreeImage_HasPixels(spBitmap))
{
return "The Image doesn't contain any pixel data! Aborting!";
}
// The Image will have the red and blue channel reversed, so we need to correct them.
SwapRedBlue32(spBitmap);
// Retrieve all the image data from FreeImage.
unsigned char *pData = FreeImage_GetBits(spBitmap);
int iWidth = FreeImage_GetWidth(spBitmap);
// Cutup the PNG.
int iFontElementSize = (32*32)*4; // The first two numbers, are the dimensions fo the element, the last number (4) is the number of color channels (Red Green Blue and Alpha)
bool bDone = false; // This bit is only set when the entire image has been loaded.
unsigned char *pElemBuff = new unsigned char[iFontElementSize]; // The temporary element buffer.
int iDataSeek = 4; // Start with an offset of 4 because the first byte of image data starts there.
int iTexIdx = 0; // This is an offset specifing which texture to create/bind to.
// Create all 36 OpenGL texures. 0-9 and A-Z and finally space (' ')
glGenTextures(37, miTextIDs);
while(!bDone)
{
// Now load the an element into the buffer.
for(int i = 0, iXCount = 0, iYCount = 0;
i < iFontElementSize; ++i, ++iXCount)
{
if(iXCount >= (32*4))
{
iXCount = 0; // Reset the column offset.
++iYCount; // Move down 1 row.
iDataSeek += ((iWidth * 4) - (32*4)); // Set the data seek to the next corrosponding piece of image data.
}
if(pData[iDataSeek] == NULL)
{
break;
}
pElemBuff[i] = pData[iDataSeek];
}
// Check to see if we are done loading to prevent memory corruption and leakage.
if(bDone || iTexIdx >= 37)
{
break;
}
// Create The OpenGL Texture with the current Element.
glBindTexture(GL_TEXTURE_2D, miTextIDs[iTexIdx]);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, 32, 32, GL_RGBA, GL_UNSIGNED_BYTE, pElemBuff);
// Create the correct texture envirnment to the current texture.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
}
// Do a little house cleaning!
delete[] pElemBuff;
delete pData;
FreeImage_Unload(spBitmap);
FreeImage_DeInitialise();
}
}
PLEASE NOTE: this code hasn't been tested yet but compile fine (according to MSVC 2008)
I forgot to change the system("sleep 1"); to system("PAUSE"); and the reason I had it invoking that command is because I originally was building this in linux.
EDIT 2: I have updated the implementation code to reflect the file.

C++ OpenGL TGA Loading Failing

I've been working through a basic OpenGl tutorial on loading a TGA file, to be used as a texture on a 3d object. I've been able to load data from the TGA header, but when I attempt to load the actual image data, it fails. I'm not sure where it is going wrong. Here is my texture loading class:
Header file:
struct TGA_Header
{
GLbyte ID_Length;
GLbyte ColorMapType;
GLbyte ImageType;
// Color map specifications
GLbyte firstEntryIndex[2];
GLbyte colorMapLength[2];
GLbyte colorMapEntrySize;
//image specification
GLshort xOrigin;
GLshort yOrigin;
GLshort ImageWidth;
GLshort ImageHeight;
GLbyte PixelDepth;
GLbyte ImageDescriptor;
};
class Texture
{
public:
Texture(string in_filename, string in_name = "");
~Texture();
public:
unsigned short width;
unsigned short height;
unsigned int length;
unsigned char type;
unsigned char *imageData;
unsigned int bpp;
unsigned int texID;
string name;
static vector<Texture *> textures;
private:
bool loadTGA(string filename);
bool createTexture(unsigned char *imageData, int width, int height, int type);
void swap(unsigned char * ori, unsigned char * dest, GLint size);
void flipImage(unsigned char * image, bool flipHorizontal, bool flipVertical, GLushort width, GLushort height, GLbyte bpp);
};
Here is the load TGA function in the cpp:
bool Texture::loadTGA(string filename)
{
TGA_Header TGAheader;
ifstream file( filename.data(), std::ios::in, std::ios::binary );
//make sure the file was opened properly
if (!file.is_open() )
return false;
if( !file.read( (char *)&TGAheader, sizeof(TGAheader) ) )
return false;
//make sure the image is of a type we can handle
if( TGAheader.ImageType != 2 )
return false;
width = TGAheader.ImageWidth;
height = TGAheader.ImageHeight;
bpp = TGAheader.PixelDepth;
if( width < 0 || // if the width or height is less than 0, than
height <= 0 || // the image is corrupt
(bpp != 24 && bpp != 32) ) // make sure we are of the correct bit depth
{
return false;
}
//check for an alpha channel
GLuint type = GL_RGBA;
if ( bpp == 24 )
type = GL_RGB;
GLuint bytesPerPixel = bpp / 8;
//allocate memory for the TGA so we can read it
GLuint imageSize = width * height * bytesPerPixel;
imageData = new GLubyte[imageSize];
if ( imageData == NULL )
return false;
//make sure we are in the correct position to load the image data
file.seekg(-imageSize, std::ios::end);
// if something when wrong, make sure we free up the memory
//NOTE: It never gets past this point. The conditional always fails.
if ( !file.read( (char *)imageData, imageSize ) )
{
delete imageData;
return false;
}
//more code is down here, but it doesnt matter because it does not pass the above function
}
It seems to load some data, but it keeps returning that it failed. Any help on why would be greatly appreciated. Appologies if it gets a bit wordy, but I'm not sure what is or is not significant.
UPDATE:
So, I just rewrote the function. The ifsteam I was using, seemed to be the cause of the problem. Specifically, it would try and load far more bytes of data than I had entered. I don't know the cause of the behavior, but I've listed my functioning code below. Thank you every one for your help.
The problem could be depending on the TGA algorithm which do not support compressed TGA.
Make sure you do not compress the TGA and that the TGA order (less important) is in a Bottom Left origin.
I usually work with GIMP and at the moment of the same, uncheck the RLE compression and put the Bottom Left alignment.
I'm not familiar with C++, sorry.
Are you sure this line file.seekg(-imageSize, std::ios::end); is not supposed to be file.seekg(headerSize, std::ios::start); ?
Makes more sense to seek from start than from end.
You should also check for ColorMapType != 0.
P.S. Here if( width < 0 || height <=0 width check should be <= as well.
So, I changed from using an ifstream to a FILE. The ifstream, was trying to load far more bytes than I had listed in the arguments. Here is the new code. (NOTE: It still needs optomized. I believe there are some unused variables floating around, but it works perfectly.). Thanks again everyone for your help.
The header file:
//struct to hold tga data
struct TGA_Header
{
GLbyte ID_Length;
GLbyte ColorMapType;
GLbyte ImageType;
// Color map specifications
GLbyte firstEntryIndex[2];
GLbyte colorMapLength[2];
GLbyte colorMapEntrySize;
//image specification
GLshort xOrigin;
GLshort yOrigin;
GLshort ImageWidth;
GLshort ImageHeight;
GLbyte PixelDepth;
GLbyte ImageDescriptor;
};
class Texture
{
public:
//functions
Texture(string in_filename, string in_name = "");
~Texture();
public:
//vars
unsigned char *imageData;
unsigned int texID;
string name;
//temp global access point for accessing all loaded textures
static vector<Texture *> textures;
private:
//can add additional load functions for other image types
bool loadTGA(string filename);
bool createTexture(unsigned char *imageData, int width, int height, int type);
void swap(unsigned char * ori, unsigned char * dest, GLint size);
void flipImage(unsigned char * image, bool flipHorizontal, bool flipVertical, GLushort width, GLushort height, GLbyte bpp);
};
#endif
Here is the load TGA function:
bool Texture::loadTGA(string filename)
{
//var for swapping colors
unsigned char colorSwap = 0;
GLuint type;
TGA_Header TGAheader;
FILE* file = fopen(filename.c_str(), "rb");
unsigned char Temp_TGAheader[18];
//check to make sure the file loaded
if( file == NULL )
return false;
fread(Temp_TGAheader, 1, sizeof(Temp_TGAheader), file);
//pull out the relavent data. 2 byte data (short) must be converted
TGAheader.ID_Length = Temp_TGAheader[0];
TGAheader.ImageType = Temp_TGAheader[2];
TGAheader.ImageWidth = *static_cast<unsigned short*>(static_cast<void*>(&Temp_TGAheader[12]));
TGAheader.ImageHeight = *static_cast<unsigned short*>(static_cast<void*>(&Temp_TGAheader[14]));
TGAheader.PixelDepth = Temp_TGAheader[16];
//make sure the image is of a type we can handle
if( TGAheader.ImageType != 2 || TGAheader.ImageWidth <= 0 || TGAheader.ImageHeight <= 0 )
{
fclose(file);
return false;
}
//set the type
if ( TGAheader.PixelDepth == 32 )
{
type = GL_RGBA;
}
else if ( TGAheader.PixelDepth == 24 )
{
type = GL_RGB;
}
else
{
//incompatable image type
return false;
}
//remember bits != bytes. To convert we need to divide by 8
GLuint bytesPerPixel = TGAheader.PixelDepth / 8;
//The Memory Required For The TGA Data
unsigned int imageSize = TGAheader.ImageWidth * TGAheader.ImageHeight * bytesPerPixel;// Calculate
//request the needed memory
imageData = new GLubyte[imageSize];
if ( imageData == NULL ) // just in case
return false;
if( fread(imageData, 1, imageSize, file) != imageSize )
{
//Kill it
delete [] imageData;
fclose(file);
return false;
}
fclose(file);
for (unsigned int x = 0; x < imageSize; x +=bytesPerPixel)
{
colorSwap = imageData[x];
imageData[x] = imageData[x + 2];
imageData[x + 2] = colorSwap;
}
createTexture( imageData, TGAheader.ImageWidth, TGAheader.ImageHeight, type );
return true;
}