Nehe OpenGL Tutorial Lesson 33rd: TGA files

Source: Internet
Author: User
Tags fread

Go from "translation" Nehe OpenGL tutorial

Objective

Statement, this Nehe OpenGL tutorial series of articles by 51 blog yarin Translation (2010-08-19), this blog for reprint and a little collation and modification. Thank you for compiling Nehe's OpenGL pipeline tutorial, as well as yarn translation finishing.

Nehe OpenGL Lesson 33rd: TGA files

To load compressed and uncompressed TGA files:

In this lesson, you will learn how to load compressed and TGA files for compression, because it uses RLE compression, so it's very simple and you can quickly familiarize yourself with it.

I've seen a lot of people ask questions about TGA reads in the game development forums or elsewhere. The next program and comments will show you how to read the uncompressed TGA file and the Rle compressed file. This detailed tutorial is suitable for OpenGL, but I plan to improve it to make it more universal in the future.

We will start with two header files. The first file controls the texture structure, and in the second, the structure and variables are used for the program to read.

Just like every header file, we need some containment to prevent the file from being duplicated.

Add such a few lines to the top of the file:

#ifndef __texture_h__//See if this header file is already included
#define __TEXTURE_H__//If not, define it

Then scroll to the bottom of the program and add:

#endif//__texture_h__ End contains protection

This three-line program prevents this file from being repeatedly included. The remaining code in the file will be between the first two lines and the last line.

In this header file, we will be adding the standard header files that are required to complete each piece of work. Add the following lines after the # define __TGA_H__:

#pragma comment (lib, "OpenGL32.lib")//Link Opengl32.lib
#include <windows.h>//Standard Windows header file
#include <stdio.h>//Standard file I/O header file
#include <gl\gl.h>//Standard OpenGL header file

The first header file is the standard Windows header file, the second one is for our later file I/O, and the third is the standard OpenGL header file required by OpenGL32.lib.

We will need a piece of space to store the image data and the type required for OpenGL to generate the texture. We are going to use the following structure:

typedef struct
{
Glubyte* ImageData; Control the color values of the entire image
Gluint BPP; Control the number of bits per pixel
Gluint width; The width of the entire image
Gluint height; The height of the entire image
Gluint Texid; The texture ID required to use glbindtexture.
Gluint type; Describe the data stored in the *imagedata (Gl_rgb Or Gl_rgba)
} Texture;

Now talk about other, longer header files. Likewise we need some containment measures, which are the same as the last one above.

Next, look at the other two structures that will be used in the process of working with TGA files.

typedef struct
{
Glubyte header[12]; File header determines file type
} Tgaheader;

typedef struct
{
Glubyte Header[6]; Control of the first 6 bytes
Gluint Bytesperpixel; Number of bytes per pixel (3 or 4)
Gluint imageSize; Control the amount of memory space required to store images
Gluint type; Image Type Gl_rgb or Gl_rgba
Gluint Height; The height of the image
Gluint Width; Image width
Gluint Bpp; Number of bits per pixel (24 or 32)
} TGA;

Now we declare some examples of the two structures so that we can use them in the program.

Tgaheader Tgaheader; To store our file headers.
TGA TGA; Used to store file information

We need to define a pair of file headers so that we can tell the program what type of file header is on a valid image. If it is an uncompressed TGA image, the first 12 bytes will be 0 0 2 0 0 0 0 0 0 0 0 0, if RLE compressed, then 0 0 10 0 0 0 0 0 0 0 0 0. These two values allow us to check if the file being read is valid.

Non-compressed TGA head
Glubyte utgacompare[12] = {0, 0, 2,0,0,0,0,0,0,0,0,0};
Compressed TGA head
Glubyte ctgacompare[12] = {0,0,10,0,0,0,0,0,0,0,0,0};

Finally, we declare two functions for the reading process.

To read an uncompressed file
BOOL Loaduncompressedtga (Texture *, char *, FILE *);
To read a compressed file
BOOL Loadcompressedtga (Texture *, char *, FILE *);

Now, back to the CPP file, and the real first part of the program, I'll save some error message handling code and make the tutorials shorter and more readable. You can see the files included in the tutorial (there are links at the end of the article).

Immediately, we can include the header file we just created at the beginning of the file.

#include "tga.h"//contains the header file we just created

No, we don't need to include any other files, because we've already included them in the header file we just finished.

The next thing we want to do is look at the first function, named Loadtga (...).

Read a TGA file!
BOOL Loadtga (Texture * Texture, char * filename)
{

It has two parameters. The former is a pointer to the texture structure, and you must declare it in your code (see included examples). The latter is a string that tells the computer where to find your texture file.

The first two lines of the function declare a file pointer, and then open the file specified by the "filename" parameter, which is passed in by the second pointer of the function.

FILE * FTGA; declaring file pointers
FTGA = fopen (filename, "RB"); Open a file in read mode

The next few lines check to see if the specified file has been opened correctly.

if (ftga = = NULL)//If there is an error here
{
... Error code ...
return false; Returns False
}

Next, we try to read the first 12 bytes of the file and store them in our tgaheader structure so that we can check the file type. If fread fails, the file is closed, an error is displayed, and the function returns FALSE.

if (fread (&tgaheader, sizeof (Tgaheader), 1, ftga) = = 0)
{
... Error code here ...
return false; Returns False if it fails
}

Next, we continue to try to determine the file type through the head we just read with the hard-made program. This can tell us that it is a compressed, uncompressed, or even wrong file type. To achieve this, we will use MEMCMP (...). Function.

If the file header is attached to the uncompressed file header format
if (memcmp (Utgacompare, &tgaheader, sizeof (tgaheader)) = = 0)
{
Read the uncompressed TGA file
LOADUNCOMPRESSEDTGA (texture, filename, ftga);
}
If file header is attached to compressed header format
else if (memcmp (Ctgacompare, &tgaheader, sizeof (tgaheader)) = = 0)
{
Read the compressed TGA format
LOADCOMPRESSEDTGA (texture, filename, ftga);
}
else//If either one does not meet
{
... Error code here ...
return false; Returns False
}

We're going to start reading a section of an uncompressed format file.

Here's the first thing we're going to do, as usual, is the function header.

Read the uncompressed TGA file
BOOL Loaduncompressedtga (Texture * Texture, char * filename, FILE * ftga)
{

This function has 3 parameters. The first two are just as simple as the loadtga. The third is a file pointer from the previous function, so we didn't lose our space.

Next we try to read 6 bytes of content from a file and store it in Tga.header. If he fails, we run some error handling code and return FALSE.

Attempt to continue reading 6 bytes of content
if (fread (Tga.header, sizeof (Tga.header), 1, ftga) = = 0)
{
... Error code here ...
return false; Returns False
}

Now we have all the information to calculate the height, width, and bpp of the image. We will store it in both the texture and the local structure.

Texture->width = tga.header[1] * + tga.header[0]; Calculate height
Texture->height = tga.header[3] * + tga.header[2]; Calculate width
TEXTURE->BPP = tga.header[4]; Calculate BPP
Tga.    Width = texture->width; Copy width to local structure
Tga.   Height = texture->height; Copy the height to the local structure.
Tga.    BPP = texture->bpp; Copy the BPP to the local structure.

Now we need to confirm that the height and width are at least 1 pixels, and that the BPP is 24 or 32. If any of these values exceed their bounds, we will display an error again, close the file, and leave the function.

Make sure all the information is valid.
if ((texture->width <= 0) | | (texture->height <= 0) | | ((texture->bpp! =) && (texture->bpp!=32)))
{
... Error code here ...
return false; Returns False
}

Next we set the type of the image. The A-bit image is a gl_rgb,32 bit image that is Gl_rgba

if (texture->bpp = = 24)//Is it a bit image?
{
Texture->type = Gl_rgb; If yes, the setting type is Gl_rgb
}
else//If it is not 24bit, it must be 32bit
{
Texture->type = Gl_rgba; This sets the type to Gl_rgba
}

Now we calculate the number of bytes per pixel and the total image data.

Tga.bytesperpixel = (TGA.  BPP/8); Calculate BPP
Calculate the memory required to store the image
Tga.imagesize = (Tga.bytesperpixel * TGA. Width * TGA. Height);

We need some space to store the entire image data, so we're going to use malloc to allocate the correct amount of memory

We then confirm that the memory has been allocated and that it is not null. If an error occurs, run the error-handling code.

Allocating memory
Texture->imagedata = (Glubyte *) malloc (tga.imagesize);
if (Texture->imagedata = = NULL)//confirm already assigned success
{
... Error code here ...
return false; Confirm that the assignment was successful
}

Here we try to read all the image data. If not, we will trigger the error-handling code again.

Attempt to read all image data
if (Fread (Texture->imagedata, 1, tga.imagesize, ftga)! = tga.imagesize)
{
... Error code here ...
return false; If not, return false
}

TGA files store images in the order of inverse OpenGL requirements, so we must format them from BGR to RGB. To achieve this, we exchange the contents of the first and third bytes of each pixel.

Steve Thomas added: I've written code that can read TGA files a little more quickly. It involves the method of converting BGR to RGB with only 3 binary operations.

Then we close the file and exit the function successfully.

Start loop
for (Gluint cswap = 0; cswap < (int) tga.imagesize; Cswap + = Tga.bytesperpixel)
{
First byte XOR third byte XOR first byte XOR third byte
Texture->imagedata[cswap] ^= texture->imagedata[cswap+2] ^=
Texture->imagedata[cswap] ^= texture->imagedata[cswap+2];
}

Fclose (FTGA); Close File
return true; Return success
}

The above is the method of reading the uncompressed TGA file. The steps to read the RLE compressed file are slightly more difficult. We read the file header as usual and collect the height/width/color depth, which is consistent with reading the uncompressed version.

BOOL Loadcompressedtga (Texture * Texture, char * filename, FILE * ftga)
{
if (fread (Tga.header, sizeof (Tga.header), 1, ftga) = = 0)
{
... Error code here ...
}
Texture->width = tga.header[1] * + tga.header[0];
Texture->height = tga.header[3] * + tga.header[2];
TEXTURE-&GT;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! =) && (texture->bpp!=32)))
{
... Error code here ...
}        }
Tga.bytesperpixel = (TGA. BPP/8);
Tga.imagesize = (Tga.bytesperpixel * TGA. Width * TGA. Height);

Now we need to allocate the space needed to store the image, which is prepared for our decompression and we will use malloc. If the memory allocation fails, run the error-handling code and return FALSE.

Allocating the memory space required to store images
Texture->imagedata = (Glubyte *) malloc (tga.imagesize);
if (Texture->imagedata = = NULL)//If memory cannot be allocated
{
... Error code here ...
return false; Returns False
}

Next we need to decide the number of pixels to make up the image. We store it in the variable "Pixelcount".

We also need to store the current pixels, as well as the bytes of the image data we are writing, so as to avoid overflow writing too much old data.

We are going to allocate enough memory to store a pixel.

Gluint Pixelcount = tga. Height * TGA. Width; Number of pixels in an image
Gluint currentpixel = 0; Pixels currently being read
Gluint currentbyte = 0; Pixels currently being written to the image
Storage space for one pixel
Glubyte * Colorbuffer = (Glubyte *) malloc (tga.bytesperpixel);

Next we're going to have a big loop.

Let's break it down into more manageable chunks.

First we declare a variable to store the "block" header. The size indicates whether the next segment is RLE or raw, and how long it is. If a byte header is less than or equal to 127, then it is a raw header. The value of the header is the number of colors, is negative, and before we process the other head bytes, we read it and copy it into memory. So we add 1 to the value we get, and then we read a lot of pixels and copy them to ImageData, just as we do with uncompressed images. If the header is greater than 127, then it is the number of times the next pixel value will be repeated. To get the actual number of repetitions, we subtract it by 127 to remove the 1bit header identifier. Then we read the next pixel and copy it sequentially to memory in accordance with the above number of times.

Do//Start loop
{
Glubyte chunkheader = 0; Variables that store ID block values
if (fread (&chunkheader, sizeof (Glubyte), 1, ftga) = = 0)//try to read the header of the block
{
... Error code ...
return false; If It fails, Return False
}

Next we're going to see if it's a raw header. If so, we need to add the value of this variable by 1 to get the total number of pixels immediately following the header.

if (Chunkheader < 128)//If it is a raw block
{
chunkheader++; Variable value plus 1 to get the total number of raw pixels

We turn on another loop to read all the color information. It loops the number of times specified in the header and reads and stores one pixel at a time.

First, we read and validate the pixel data. The data for a single pixel is stored in the Colorbuffer variable. Then we will check if it is raw header. If so, we need to add one to the variable to get the total number of pixels after the header.

Start pixel read loop
for (short counter = 0; counter < Chunkheader; counter++)
{
Try to read a pixel
if (Fread (Colorbuffer, 1, Tga.bytesperpixel, ftga)! = Tga.bytesperpixel)
{
... Error code ...
return false; If it fails, returns false
}

The next step in our loop is to get the color value stored in Colorbuffer and write it to the ImageData variable that will be used later. In this process, the data format will be flipped from BGR to RGB or converted from Bgra to Rgba, depending on the number of bits per pixel. When we finish the task we increase the current byte and the current pixel counter.

Texture->imagedata[currentbyte] = colorbuffer[2]; Write "R" bytes
Texture->imagedata[currentbyte + 1] = colorbuffer[1]; Write "G" bytes
Texture->imagedata[currentbyte + 2] = colorbuffer[0]; Write "B" bytes
if (Tga.bytesperpixel = = 4)//If it is a 32-bit image ...
{
Texture->imagedata[currentbyte + 3] = colorbuffer[3]; Write "A" byte
}
Increase the byte counter based on the number of bytes per pixel
Currentbyte + = Tga.bytesperpixel;
currentpixel++; Pixel counter plus 1

The next paragraph describes the "block" header of the RLE segment. First we subtract the chunkheader minus 127来 to get the number of times the next color repeats.

else//If the RLE head
{
Chunkheader-= 127; Minus 127 Get the rid of the ID bit

Then we try to read the next color value.

Read Next pixel
if (Fread (Colorbuffer, 1, Tga.bytesperpixel, ftga)! = Tga.bytesperpixel)
{
... Error code ...
return false; If it fails, returns false
}

Next, we start looping through the memory of the pixels we read several times, which is defined by the values in the RLE header.

We then copy the color values into the image data and preprocess the value exchange of R and B.

We then increase the current number of bytes, the current pixel, so that we can be in the right place when we write the value again.

Start loop
for (short counter = 0; counter < Chunkheader; counter++)
{
Copy "R" bytes
Texture->imagedata[currentbyte] = colorbuffer[2];
Copy "G" bytes
Texture->imagedata[currentbyte + 1] = colorbuffer[1];
Copy "B" bytes
Texture->imagedata[currentbyte + 2] = colorbuffer[0];
if (Tga.bytesperpixel = = 4)//If it is a 32-bit image
{
Copy "A" byte
Texture->imagedata[currentbyte + 3] = colorbuffer[3];
}
Currentbyte + = Tga.bytesperpixel; Increase byte counter
currentpixel++; Increase byte counter

As long as there are still pixels left to read, we will continue the main loop.

Finally, we close the file and return to success.

while (Currentpixel < pixelcount); Do you have more pixels to read? Start the loop until the end
Fclose (FTGA); Close File
return true; Return success
}
Original source code and version of the download:

Http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=33

Nehe OpenGL Tutorial Lesson 33rd: TGA files

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.