This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* tga.c -- tga texture loader | |
* last modification: aug. 14, 2007 | |
* | |
* Copyright (c) 2005-2007 David HENRY | |
* | |
* Permission is hereby granted, free of charge, to any person | |
* obtaining a copy of this software and associated documentation | |
* files (the "Software"), to deal in the Software without | |
* restriction, including without limitation the rights to use, | |
* copy, modify, merge, publish, distribute, sublicense, and/or | |
* sell copies of the Software, and to permit persons to whom the | |
* Software is furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be | |
* included in all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
* NONINFRINGEMENT. | |
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR | |
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
* | |
* gcc -Wall -ansi -lGL -lGLU -lglut tga.c -o tga | |
*/ | |
#include <GL/glut.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
/* OpenGL texture info */ | |
struct gl_texture_t | |
{ | |
GLsizei width; | |
GLsizei height; | |
GLenum format; | |
GLint internalFormat; | |
GLuint id; | |
GLubyte *texels; | |
}; | |
#pragma pack(push, 1) | |
/* TGA header */ | |
struct tga_header_t | |
{ | |
GLubyte id_lenght; /* size of image id */ | |
GLubyte colormap_type; /* 1 is has a colormap */ | |
GLubyte image_type; /* compression type */ | |
short cm_first_entry; /* colormap origin */ | |
short cm_length; /* colormap length */ | |
GLubyte cm_size; /* colormap size */ | |
short x_origin; /* bottom left x coord origin */ | |
short y_origin; /* bottom left y coord origin */ | |
short width; /* picture width (in pixels) */ | |
short height; /* picture height (in pixels) */ | |
GLubyte pixel_depth; /* bits per pixel: 8, 16, 24 or 32 */ | |
GLubyte image_descriptor; /* 24 bits = 0x00; 32 bits = 0x80 */ | |
}; | |
#pragma pack(pop) | |
/* Texture id for the demo */ | |
GLuint texId = 0; | |
static void | |
GetTextureInfo (const struct tga_header_t *header, | |
struct gl_texture_t *texinfo) | |
{ | |
texinfo->width = header->width; | |
texinfo->height = header->height; | |
switch (header->image_type) | |
{ | |
case 3: /* Grayscale 8 bits */ | |
case 11: /* Grayscale 8 bits (RLE) */ | |
{ | |
if (header->pixel_depth == 8) | |
{ | |
texinfo->format = GL_LUMINANCE; | |
texinfo->internalFormat = 1; | |
} | |
else /* 16 bits */ | |
{ | |
texinfo->format = GL_LUMINANCE_ALPHA; | |
texinfo->internalFormat = 2; | |
} | |
break; | |
} | |
case 1: /* 8 bits color index */ | |
case 2: /* BGR 16-24-32 bits */ | |
case 9: /* 8 bits color index (RLE) */ | |
case 10: /* BGR 16-24-32 bits (RLE) */ | |
{ | |
/* 8 bits and 16 bits images will be converted to 24 bits */ | |
if (header->pixel_depth <= 24) | |
{ | |
texinfo->format = GL_RGB; | |
texinfo->internalFormat = 3; | |
} | |
else /* 32 bits */ | |
{ | |
texinfo->format = GL_RGBA; | |
texinfo->internalFormat = 4; | |
} | |
break; | |
} | |
} | |
} | |
static void | |
ReadTGA8bits (FILE *fp, const GLubyte *colormap, | |
struct gl_texture_t *texinfo) | |
{ | |
int i; | |
GLubyte color; | |
for (i = 0; i < texinfo->width * texinfo->height; ++i) | |
{ | |
/* Read index color byte */ | |
color = (GLubyte)fgetc (fp); | |
/* Convert to RGB 24 bits */ | |
texinfo->texels[(i * 3) + 2] = colormap[(color * 3) + 0]; | |
texinfo->texels[(i * 3) + 1] = colormap[(color * 3) + 1]; | |
texinfo->texels[(i * 3) + 0] = colormap[(color * 3) + 2]; | |
} | |
} | |
static void | |
ReadTGA16bits (FILE *fp, struct gl_texture_t *texinfo) | |
{ | |
int i; | |
unsigned short color; | |
for (i = 0; i < texinfo->width * texinfo->height; ++i) | |
{ | |
/* Read color word */ | |
color = fgetc (fp) + (fgetc (fp) << 8); | |
/* Convert BGR to RGB */ | |
texinfo->texels[(i * 3) + 0] = (GLubyte)(((color & 0x7C00) >> 10) << 3); | |
texinfo->texels[(i * 3) + 1] = (GLubyte)(((color & 0x03E0) >> 5) << 3); | |
texinfo->texels[(i * 3) + 2] = (GLubyte)(((color & 0x001F) >> 0) << 3); | |
} | |
} | |
static void | |
ReadTGA24bits (FILE *fp, struct gl_texture_t *texinfo) | |
{ | |
int i; | |
for (i = 0; i < texinfo->width * texinfo->height; ++i) | |
{ | |
/* Read and convert BGR to RGB */ | |
texinfo->texels[(i * 3) + 2] = (GLubyte)fgetc (fp); | |
texinfo->texels[(i * 3) + 1] = (GLubyte)fgetc (fp); | |
texinfo->texels[(i * 3) + 0] = (GLubyte)fgetc (fp); | |
} | |
} | |
static void | |
ReadTGA32bits (FILE *fp, struct gl_texture_t *texinfo) | |
{ | |
int i; | |
for (i = 0; i < texinfo->width * texinfo->height; ++i) | |
{ | |
/* Read and convert BGRA to RGBA */ | |
texinfo->texels[(i * 4) + 2] = (GLubyte)fgetc (fp); | |
texinfo->texels[(i * 4) + 1] = (GLubyte)fgetc (fp); | |
texinfo->texels[(i * 4) + 0] = (GLubyte)fgetc (fp); | |
texinfo->texels[(i * 4) + 3] = (GLubyte)fgetc (fp); | |
} | |
} | |
static void | |
ReadTGAgray8bits (FILE *fp, struct gl_texture_t *texinfo) | |
{ | |
int i; | |
for (i = 0; i < texinfo->width * texinfo->height; ++i) | |
{ | |
/* Read grayscale color byte */ | |
texinfo->texels[i] = (GLubyte)fgetc (fp); | |
} | |
} | |
static void | |
ReadTGAgray16bits (FILE *fp, struct gl_texture_t *texinfo) | |
{ | |
int i; | |
for (i = 0; i < texinfo->width * texinfo->height; ++i) | |
{ | |
/* Read grayscale color + alpha channel bytes */ | |
texinfo->texels[(i * 2) + 0] = (GLubyte)fgetc (fp); | |
texinfo->texels[(i * 2) + 1] = (GLubyte)fgetc (fp); | |
} | |
} | |
static void | |
ReadTGA8bitsRLE (FILE *fp, const GLubyte *colormap, | |
struct gl_texture_t *texinfo) | |
{ | |
int i, size; | |
GLubyte color; | |
GLubyte packet_header; | |
GLubyte *ptr = texinfo->texels; | |
while (ptr < texinfo->texels + (texinfo->width * texinfo->height) * 3) | |
{ | |
/* Read first byte */ | |
packet_header = (GLubyte)fgetc (fp); | |
size = 1 + (packet_header & 0x7f); | |
if (packet_header & 0x80) | |
{ | |
/* Run-length packet */ | |
color = (GLubyte)fgetc (fp); | |
for (i = 0; i < size; ++i, ptr += 3) | |
{ | |
ptr[0] = colormap[(color * 3) + 2]; | |
ptr[1] = colormap[(color * 3) + 1]; | |
ptr[2] = colormap[(color * 3) + 0]; | |
} | |
} | |
else | |
{ | |
/* Non run-length packet */ | |
for (i = 0; i < size; ++i, ptr += 3) | |
{ | |
color = (GLubyte)fgetc (fp); | |
ptr[0] = colormap[(color * 3) + 2]; | |
ptr[1] = colormap[(color * 3) + 1]; | |
ptr[2] = colormap[(color * 3) + 0]; | |
} | |
} | |
} | |
} | |
static void | |
ReadTGA16bitsRLE (FILE *fp, struct gl_texture_t *texinfo) | |
{ | |
int i, size; | |
unsigned short color; | |
GLubyte packet_header; | |
GLubyte *ptr = texinfo->texels; | |
while (ptr < texinfo->texels + (texinfo->width * texinfo->height) * 3) | |
{ | |
/* Read first byte */ | |
packet_header = fgetc (fp); | |
size = 1 + (packet_header & 0x7f); | |
if (packet_header & 0x80) | |
{ | |
/* Run-length packet */ | |
color = fgetc (fp) + (fgetc (fp) << 8); | |
for (i = 0; i < size; ++i, ptr += 3) | |
{ | |
ptr[0] = (GLubyte)(((color & 0x7C00) >> 10) << 3); | |
ptr[1] = (GLubyte)(((color & 0x03E0) >> 5) << 3); | |
ptr[2] = (GLubyte)(((color & 0x001F) >> 0) << 3); | |
} | |
} | |
else | |
{ | |
/* Non run-length packet */ | |
for (i = 0; i < size; ++i, ptr += 3) | |
{ | |
color = fgetc (fp) + (fgetc (fp) << 8); | |
ptr[0] = (GLubyte)(((color & 0x7C00) >> 10) << 3); | |
ptr[1] = (GLubyte)(((color & 0x03E0) >> 5) << 3); | |
ptr[2] = (GLubyte)(((color & 0x001F) >> 0) << 3); | |
} | |
} | |
} | |
} | |
static void | |
ReadTGA24bitsRLE (FILE *fp, struct gl_texture_t *texinfo) | |
{ | |
int i, size; | |
GLubyte rgb[3]; | |
GLubyte packet_header; | |
GLubyte *ptr = texinfo->texels; | |
while (ptr < texinfo->texels + (texinfo->width * texinfo->height) * 3) | |
{ | |
/* Read first byte */ | |
packet_header = (GLubyte)fgetc (fp); | |
size = 1 + (packet_header & 0x7f); | |
if (packet_header & 0x80) | |
{ | |
/* Run-length packet */ | |
fread (rgb, sizeof (GLubyte), 3, fp); | |
for (i = 0; i < size; ++i, ptr += 3) | |
{ | |
ptr[0] = rgb[2]; | |
ptr[1] = rgb[1]; | |
ptr[2] = rgb[0]; | |
} | |
} | |
else | |
{ | |
/* Non run-length packet */ | |
for (i = 0; i < size; ++i, ptr += 3) | |
{ | |
ptr[2] = (GLubyte)fgetc (fp); | |
ptr[1] = (GLubyte)fgetc (fp); | |
ptr[0] = (GLubyte)fgetc (fp); | |
} | |
} | |
} | |
} | |
static void | |
ReadTGA32bitsRLE (FILE *fp, struct gl_texture_t *texinfo) | |
{ | |
int i, size; | |
GLubyte rgba[4]; | |
GLubyte packet_header; | |
GLubyte *ptr = texinfo->texels; | |
while (ptr < texinfo->texels + (texinfo->width * texinfo->height) * 4) | |
{ | |
/* Read first byte */ | |
packet_header = (GLubyte)fgetc (fp); | |
size = 1 + (packet_header & 0x7f); | |
if (packet_header & 0x80) | |
{ | |
/* Run-length packet */ | |
fread (rgba, sizeof (GLubyte), 4, fp); | |
for (i = 0; i < size; ++i, ptr += 4) | |
{ | |
ptr[0] = rgba[2]; | |
ptr[1] = rgba[1]; | |
ptr[2] = rgba[0]; | |
ptr[3] = rgba[3]; | |
} | |
} | |
else | |
{ | |
/* Non run-length packet */ | |
for (i = 0; i < size; ++i, ptr += 4) | |
{ | |
ptr[2] = (GLubyte)fgetc (fp); | |
ptr[1] = (GLubyte)fgetc (fp); | |
ptr[0] = (GLubyte)fgetc (fp); | |
ptr[3] = (GLubyte)fgetc (fp); | |
} | |
} | |
} | |
} | |
static void | |
ReadTGAgray8bitsRLE (FILE *fp, struct gl_texture_t *texinfo) | |
{ | |
int i, size; | |
GLubyte color; | |
GLubyte packet_header; | |
GLubyte *ptr = texinfo->texels; | |
while (ptr < texinfo->texels + (texinfo->width * texinfo->height)) | |
{ | |
/* Read first byte */ | |
packet_header = (GLubyte)fgetc (fp); | |
size = 1 + (packet_header & 0x7f); | |
if (packet_header & 0x80) | |
{ | |
/* Run-length packet */ | |
color = (GLubyte)fgetc (fp); | |
for (i = 0; i < size; ++i, ptr++) | |
*ptr = color; | |
} | |
else | |
{ | |
/* Non run-length packet */ | |
for (i = 0; i < size; ++i, ptr++) | |
*ptr = (GLubyte)fgetc (fp); | |
} | |
} | |
} | |
static void | |
ReadTGAgray16bitsRLE (FILE *fp, struct gl_texture_t *texinfo) | |
{ | |
int i, size; | |
GLubyte color, alpha; | |
GLubyte packet_header; | |
GLubyte *ptr = texinfo->texels; | |
while (ptr < texinfo->texels + (texinfo->width * texinfo->height) * 2) | |
{ | |
/* Read first byte */ | |
packet_header = (GLubyte)fgetc (fp); | |
size = 1 + (packet_header & 0x7f); | |
if (packet_header & 0x80) | |
{ | |
/* Run-length packet */ | |
color = (GLubyte)fgetc (fp); | |
alpha = (GLubyte)fgetc (fp); | |
for (i = 0; i < size; ++i, ptr += 2) | |
{ | |
ptr[0] = color; | |
ptr[1] = alpha; | |
} | |
} | |
else | |
{ | |
/* Non run-length packet */ | |
for (i = 0; i < size; ++i, ptr += 2) | |
{ | |
ptr[0] = (GLubyte)fgetc (fp); | |
ptr[1] = (GLubyte)fgetc (fp); | |
} | |
} | |
} | |
} | |
static struct gl_texture_t * | |
ReadTGAFile (const char *filename) | |
{ | |
FILE *fp; | |
struct gl_texture_t *texinfo; | |
struct tga_header_t header; | |
GLubyte *colormap = NULL; | |
fp = fopen (filename, "rb"); | |
if (!fp) | |
{ | |
fprintf (stderr, "error: couldn't open \"%s\"!\n", filename); | |
return NULL; | |
} | |
/* Read header */ | |
fread (&header, sizeof (struct tga_header_t), 1, fp); | |
texinfo = (struct gl_texture_t *) | |
malloc (sizeof (struct gl_texture_t)); | |
GetTextureInfo (&header, texinfo); | |
fseek (fp, header.id_lenght, SEEK_CUR); | |
/* Memory allocation */ | |
texinfo->texels = (GLubyte *)malloc (sizeof (GLubyte) * | |
texinfo->width * texinfo->height * texinfo->internalFormat); | |
if (!texinfo->texels) | |
{ | |
free (texinfo); | |
return NULL; | |
} | |
/* Read color map */ | |
if (header.colormap_type) | |
{ | |
/* NOTE: color map is stored in BGR format */ | |
colormap = (GLubyte *)malloc (sizeof (GLubyte) | |
* header.cm_length * (header.cm_size >> 3)); | |
fread (colormap, sizeof (GLubyte), header.cm_length | |
* (header.cm_size >> 3), fp); | |
} | |
/* Read image data */ | |
switch (header.image_type) | |
{ | |
case 0: | |
/* No data */ | |
break; | |
case 1: | |
/* Uncompressed 8 bits color index */ | |
ReadTGA8bits (fp, colormap, texinfo); | |
break; | |
case 2: | |
/* Uncompressed 16-24-32 bits */ | |
switch (header.pixel_depth) | |
{ | |
case 16: | |
ReadTGA16bits (fp, texinfo); | |
break; | |
case 24: | |
ReadTGA24bits (fp, texinfo); | |
break; | |
case 32: | |
ReadTGA32bits (fp, texinfo); | |
break; | |
} | |
break; | |
case 3: | |
/* Uncompressed 8 or 16 bits grayscale */ | |
if (header.pixel_depth == 8) | |
ReadTGAgray8bits (fp, texinfo); | |
else /* 16 */ | |
ReadTGAgray16bits (fp, texinfo); | |
break; | |
case 9: | |
/* RLE compressed 8 bits color index */ | |
ReadTGA8bitsRLE (fp, colormap, texinfo); | |
break; | |
case 10: | |
/* RLE compressed 16-24-32 bits */ | |
switch (header.pixel_depth) | |
{ | |
case 16: | |
ReadTGA16bitsRLE (fp, texinfo); | |
break; | |
case 24: | |
ReadTGA24bitsRLE (fp, texinfo); | |
break; | |
case 32: | |
ReadTGA32bitsRLE (fp, texinfo); | |
break; | |
} | |
break; | |
case 11: | |
/* RLE compressed 8 or 16 bits grayscale */ | |
if (header.pixel_depth == 8) | |
ReadTGAgray8bitsRLE (fp, texinfo); | |
else /* 16 */ | |
ReadTGAgray16bitsRLE (fp, texinfo); | |
break; | |
default: | |
/* Image type is not correct */ | |
fprintf (stderr, "error: unknown TGA image type %i!\n", header.image_type); | |
free (texinfo->texels); | |
free (texinfo); | |
texinfo = NULL; | |
break; | |
} | |
/* No longer need colormap data */ | |
if (colormap) | |
free (colormap); | |
fclose (fp); | |
return texinfo; | |
} | |
GLuint | |
loadTGATexture (const char *filename) | |
{ | |
struct gl_texture_t *tga_tex = NULL; | |
GLuint tex_id = 0; | |
GLint alignment; | |
tga_tex = ReadTGAFile (filename); | |
if (tga_tex && tga_tex->texels) | |
{ | |
/* Generate texture */ | |
glGenTextures (1, &tga_tex->id); | |
glBindTexture (GL_TEXTURE_2D, tga_tex->id); | |
/* Setup some parameters for texture filters and mipmapping */ | |
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); | |
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glGetIntegerv (GL_UNPACK_ALIGNMENT, &alignment); | |
glPixelStorei (GL_UNPACK_ALIGNMENT, 1); | |
#if 0 | |
glTexImage2D (GL_TEXTURE_2D, 0, tga_tex->internalFormat, | |
tga_tex->width, tga_tex->height, 0, tga_tex->format, | |
GL_UNSIGNED_BYTE, tga_tex->texels); | |
#else | |
gluBuild2DMipmaps (GL_TEXTURE_2D, tga_tex->internalFormat, | |
tga_tex->width, tga_tex->height, | |
tga_tex->format, GL_UNSIGNED_BYTE, tga_tex->texels); | |
#endif | |
glPixelStorei (GL_UNPACK_ALIGNMENT, alignment); | |
tex_id = tga_tex->id; | |
/* OpenGL has its own copy of texture data */ | |
free (tga_tex->texels); | |
free (tga_tex); | |
} | |
return tex_id; | |
} | |
static void | |
cleanup () | |
{ | |
glDeleteTextures (1, &texId); | |
} | |
static void | |
init (const char *filename) | |
{ | |
/* Initialize OpenGL */ | |
glClearColor (0.5f, 0.5f, 0.5f, 1.0f); | |
glShadeModel (GL_SMOOTH); | |
glEnable (GL_DEPTH_TEST); | |
glEnable (GL_BLEND); | |
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
/* Load TGA texture from file */ | |
texId = loadTGATexture (filename); | |
if (!texId) | |
exit (EXIT_FAILURE); | |
} | |
static void | |
reshape (int w, int h) | |
{ | |
if (h == 0) | |
h = 1; | |
glViewport (0, 0, (GLsizei)w, (GLsizei)h); | |
glMatrixMode (GL_PROJECTION); | |
glLoadIdentity (); | |
gluPerspective (45.0, w/(GLdouble)h, 0.1, 1000.0); | |
glMatrixMode (GL_MODELVIEW); | |
glLoadIdentity (); | |
glutPostRedisplay (); | |
} | |
static void | |
display () | |
{ | |
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
glLoadIdentity (); | |
glEnable (GL_TEXTURE_2D); | |
glBindTexture (GL_TEXTURE_2D, texId); | |
/* Draw textured quad */ | |
glTranslatef (0.0, 0.0, -5.0); | |
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 (); | |
glDisable (GL_TEXTURE_2D); | |
glutSwapBuffers (); | |
} | |
static void | |
keyboard (unsigned char key, int x, int y) | |
{ | |
/* Escape */ | |
if (key == 27) | |
exit (0); | |
} | |
int | |
main (int argc, char *argv[]) | |
{ | |
if (argc < 2) | |
{ | |
fprintf (stderr, "usage: %s <filename.tga>\n", argv[0]); | |
return -1; | |
} | |
glutInit (&argc, argv); | |
glutInitDisplayMode (GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); | |
glutInitWindowSize (640, 480); | |
glutCreateWindow ("TGA Texture Demo"); | |
atexit (cleanup); | |
init (argv[1]); | |
glutReshapeFunc (reshape); | |
glutDisplayFunc (display); | |
glutKeyboardFunc (keyboard); | |
glutMainLoop (); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* tga_mem.c -- tga texture loader | |
* last modification: aug. 14, 2007 | |
* | |
* Copyright (c) 2005-2007 David HENRY | |
* | |
* Permission is hereby granted, free of charge, to any person | |
* obtaining a copy of this software and associated documentation | |
* files (the "Software"), to deal in the Software without | |
* restriction, including without limitation the rights to use, | |
* copy, modify, merge, publish, distribute, sublicense, and/or | |
* sell copies of the Software, and to permit persons to whom the | |
* Software is furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be | |
* included in all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
* NONINFRINGEMENT. | |
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR | |
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
* | |
* gcc -Wall -ansi -lGL -lGLU -lglut tga_mem.c -o tga_mem | |
*/ | |
#include <GL/glut.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
/* OpenGL texture info */ | |
struct gl_texture_t | |
{ | |
GLsizei width; | |
GLsizei height; | |
GLenum format; | |
GLint internalFormat; | |
GLuint id; | |
GLubyte *texels; | |
}; | |
#pragma pack(push, 1) | |
/* TGA header */ | |
struct tga_header_t | |
{ | |
GLubyte id_lenght; /* size of image id */ | |
GLubyte colormap_type; /* 1 is has a colormap */ | |
GLubyte image_type; /* compression type */ | |
short cm_first_entry; /* colormap origin */ | |
short cm_length; /* colormap length */ | |
GLubyte cm_size; /* colormap size */ | |
short x_origin; /* bottom left x coord origin */ | |
short y_origin; /* bottom left y coord origin */ | |
short width; /* picture width (in pixels) */ | |
short height; /* picture height (in pixels) */ | |
GLubyte pixel_depth; /* bits per pixel: 8, 16, 24 or 32 */ | |
GLubyte image_descriptor; /* 24 bits = 0x00; 32 bits = 0x80 */ | |
}; | |
#pragma pack(pop) | |
/* Texture id for the demo */ | |
GLuint texId = 0; | |
static void | |
GetTextureInfo (const struct tga_header_t *header, | |
struct gl_texture_t *texinfo) | |
{ | |
texinfo->width = header->width; | |
texinfo->height = header->height; | |
switch (header->image_type) | |
{ | |
case 3: /* Grayscale 8 bits */ | |
case 11: /* Grayscale 8 bits (RLE) */ | |
{ | |
if (header->pixel_depth == 8) | |
{ | |
texinfo->format = GL_LUMINANCE; | |
texinfo->internalFormat = 1; | |
} | |
else /* 16 bits */ | |
{ | |
texinfo->format = GL_LUMINANCE_ALPHA; | |
texinfo->internalFormat = 2; | |
} | |
break; | |
} | |
case 1: /* 8 bits color index */ | |
case 2: /* BGR 16-24-32 bits */ | |
case 9: /* 8 bits color index (RLE) */ | |
case 10: /* BGR 16-24-32 bits (RLE) */ | |
{ | |
/* 8 bits and 16 bits images will be converted to 24 bits */ | |
if (header->pixel_depth <= 24) | |
{ | |
texinfo->format = GL_RGB; | |
texinfo->internalFormat = 3; | |
} | |
else /* 32 bits */ | |
{ | |
texinfo->format = GL_RGBA; | |
texinfo->internalFormat = 4; | |
} | |
break; | |
} | |
} | |
} | |
static void | |
ReadTGA8bits (const GLubyte *data, const GLubyte *colormap, | |
struct gl_texture_t *texinfo) | |
{ | |
int i; | |
GLubyte color; | |
for (i = 0; i < texinfo->width * texinfo->height; ++i) | |
{ | |
/* Read index color byte */ | |
color = data[i]; | |
/* Convert to RGB 24 bits */ | |
texinfo->texels[(i * 3) + 2] = colormap[(color * 3) + 0]; | |
texinfo->texels[(i * 3) + 1] = colormap[(color * 3) + 1]; | |
texinfo->texels[(i * 3) + 0] = colormap[(color * 3) + 2]; | |
} | |
} | |
static void | |
ReadTGA16bits (const GLubyte *data, struct gl_texture_t *texinfo) | |
{ | |
int i, j = 0; | |
unsigned short color; | |
for (i = 0; i < texinfo->width * texinfo->height; ++i, j += 2) | |
{ | |
/* Read color word */ | |
color = data[j] + (data[j + 1] << 8); | |
/* Convert BGR to RGB */ | |
texinfo->texels[(i * 3) + 0] = (GLubyte)(((color & 0x7C00) >> 10) << 3); | |
texinfo->texels[(i * 3) + 1] = (GLubyte)(((color & 0x03E0) >> 5) << 3); | |
texinfo->texels[(i * 3) + 2] = (GLubyte)(((color & 0x001F) >> 0) << 3); | |
} | |
} | |
static void | |
ReadTGA24bits (const GLubyte *data, struct gl_texture_t *texinfo) | |
{ | |
int i, j = 0; | |
for (i = 0; i < texinfo->width * texinfo->height; ++i, j += 3) | |
{ | |
/* Read and convert BGR to RGB */ | |
texinfo->texels[(i * 3) + 2] = data[j + 0]; | |
texinfo->texels[(i * 3) + 1] = data[j + 1]; | |
texinfo->texels[(i * 3) + 0] = data[j + 2]; | |
} | |
} | |
static void | |
ReadTGA32bits (const GLubyte *data, struct gl_texture_t *texinfo) | |
{ | |
int i, j = 0; | |
for (i = 0; i < texinfo->width * texinfo->height; ++i, j += 4) | |
{ | |
/* Read and convert BGRA to RGBA */ | |
texinfo->texels[(i * 4) + 2] = data[j + 0]; | |
texinfo->texels[(i * 4) + 1] = data[j + 1]; | |
texinfo->texels[(i * 4) + 0] = data[j + 2]; | |
texinfo->texels[(i * 4) + 3] = data[j + 3]; | |
} | |
} | |
static void | |
ReadTGAgray8bits (const GLubyte *data, struct gl_texture_t *texinfo) | |
{ | |
memcpy (texinfo->texels, data, sizeof (GLubyte) * | |
texinfo->width * texinfo->height); | |
} | |
static void | |
ReadTGAgray16bits (const GLubyte *data, struct gl_texture_t *texinfo) | |
{ | |
memcpy (texinfo->texels, data, sizeof (GLubyte) * | |
texinfo->width * texinfo->height * 2); | |
} | |
static void | |
ReadTGA8bitsRLE (const GLubyte *data, const GLubyte *colormap, | |
struct gl_texture_t *texinfo) | |
{ | |
int i, size, j = 0; | |
GLubyte color; | |
GLubyte packet_header; | |
GLubyte *ptr = texinfo->texels; | |
while (ptr < texinfo->texels + (texinfo->width * texinfo->height) * 3) | |
{ | |
/* Read first byte */ | |
packet_header = data[j++]; | |
size = 1 + (packet_header & 0x7f); | |
if (packet_header & 0x80) | |
{ | |
/* Run-length packet */ | |
color = data[j++]; | |
for (i = 0; i < size; ++i, ptr += 3) | |
{ | |
ptr[0] = colormap[(color * 3) + 2]; | |
ptr[1] = colormap[(color * 3) + 1]; | |
ptr[2] = colormap[(color * 3) + 0]; | |
} | |
} | |
else | |
{ | |
/* Non run-length packet */ | |
for (i = 0; i < size; ++i, ptr += 3) | |
{ | |
color = data[j++]; | |
ptr[0] = colormap[(color * 3) + 2]; | |
ptr[1] = colormap[(color * 3) + 1]; | |
ptr[2] = colormap[(color * 3) + 0]; | |
} | |
} | |
} | |
} | |
static void | |
ReadTGA16bitsRLE (const GLubyte *data, struct gl_texture_t *texinfo) | |
{ | |
int i, size, j = 0; | |
unsigned short color; | |
GLubyte packet_header; | |
GLubyte *ptr = texinfo->texels; | |
while (ptr < texinfo->texels + (texinfo->width * texinfo->height) * 3) | |
{ | |
/* Read first byte */ | |
packet_header = data[j++]; | |
size = 1 + (packet_header & 0x7f); | |
if (packet_header & 0x80) | |
{ | |
/* Run-length packet */ | |
color = data[j] + (data[j + 1] << 8); | |
j += 2; | |
for (i = 0; i < size; ++i, ptr += 3) | |
{ | |
ptr[0] = (GLubyte)(((color & 0x7C00) >> 10) << 3); | |
ptr[1] = (GLubyte)(((color & 0x03E0) >> 5) << 3); | |
ptr[2] = (GLubyte)(((color & 0x001F) >> 0) << 3); | |
} | |
} | |
else | |
{ | |
/* Non run-length packet */ | |
for (i = 0; i < size; ++i, ptr += 3, j += 2) | |
{ | |
color = data[j] + (data[j + 1] << 8); | |
ptr[0] = (GLubyte)(((color & 0x7C00) >> 10) << 3); | |
ptr[1] = (GLubyte)(((color & 0x03E0) >> 5) << 3); | |
ptr[2] = (GLubyte)(((color & 0x001F) >> 0) << 3); | |
} | |
} | |
} | |
} | |
static void | |
ReadTGA24bitsRLE (const GLubyte *data, struct gl_texture_t *texinfo) | |
{ | |
int i, size, j = 0; | |
const GLubyte *rgb; | |
GLubyte packet_header; | |
GLubyte *ptr = texinfo->texels; | |
while (ptr < texinfo->texels + (texinfo->width * texinfo->height) * 3) | |
{ | |
/* Read first byte */ | |
packet_header = data[j++]; | |
size = 1 + (packet_header & 0x7f); | |
if (packet_header & 0x80) | |
{ | |
/* Run-length packet */ | |
rgb = &data[j]; | |
j += 3; | |
for (i = 0; i < size; ++i, ptr += 3) | |
{ | |
ptr[0] = rgb[2]; | |
ptr[1] = rgb[1]; | |
ptr[2] = rgb[0]; | |
} | |
} | |
else | |
{ | |
/* Non run-length packet */ | |
for (i = 0; i < size; ++i, ptr += 3, j += 3) | |
{ | |
ptr[2] = data[j + 0]; | |
ptr[1] = data[j + 1]; | |
ptr[0] = data[j + 2]; | |
} | |
} | |
} | |
} | |
static void | |
ReadTGA32bitsRLE (const GLubyte *data, struct gl_texture_t *texinfo) | |
{ | |
int i, size, j = 0; | |
const GLubyte *rgba; | |
GLubyte packet_header; | |
GLubyte *ptr = texinfo->texels; | |
while (ptr < texinfo->texels + (texinfo->width * texinfo->height) * 4) | |
{ | |
/* Read first byte */ | |
packet_header = data[j++]; | |
size = 1 + (packet_header & 0x7f); | |
if (packet_header & 0x80) | |
{ | |
/* Run-length packet */ | |
rgba = &data[j]; | |
j += 4; | |
for (i = 0; i < size; ++i, ptr += 4) | |
{ | |
ptr[0] = rgba[2]; | |
ptr[1] = rgba[1]; | |
ptr[2] = rgba[0]; | |
ptr[3] = rgba[3]; | |
} | |
} | |
else | |
{ | |
/* Non run-length packet */ | |
for (i = 0; i < size; ++i, ptr += 4, j += 4) | |
{ | |
ptr[2] = data[j + 0]; | |
ptr[1] = data[j + 1]; | |
ptr[0] = data[j + 2]; | |
ptr[3] = data[j + 3]; | |
} | |
} | |
} | |
} | |
static void | |
ReadTGAgray8bitsRLE (const GLubyte *data, struct gl_texture_t *texinfo) | |
{ | |
int i, size, j = 0; | |
GLubyte color; | |
GLubyte packet_header; | |
GLubyte *ptr = texinfo->texels; | |
while (ptr < texinfo->texels + (texinfo->width * texinfo->height)) | |
{ | |
/* Read first byte */ | |
packet_header = data[j++]; | |
size = 1 + (packet_header & 0x7f); | |
if (packet_header & 0x80) | |
{ | |
/* Run-length packet */ | |
color = data[j++]; | |
for (i = 0; i < size; ++i, ptr++) | |
*ptr = color; | |
} | |
else | |
{ | |
/* Non run-length packet */ | |
memcpy (ptr, data + j, size * sizeof (GLubyte)); | |
ptr += size; | |
j += size; | |
} | |
} | |
} | |
static void | |
ReadTGAgray16bitsRLE (const GLubyte *data, struct gl_texture_t *texinfo) | |
{ | |
int i, size, j = 0; | |
GLubyte color, alpha; | |
GLubyte packet_header; | |
GLubyte *ptr = texinfo->texels; | |
while (ptr < texinfo->texels + (texinfo->width * texinfo->height) * 2) | |
{ | |
/* Read first byte */ | |
packet_header = data[j++]; | |
size = 1 + (packet_header & 0x7f); | |
if (packet_header & 0x80) | |
{ | |
/* Run-length packet */ | |
color = data[j++]; | |
alpha = data[j++]; | |
for (i = 0; i < size; ++i, ptr += 2) | |
{ | |
ptr[0] = color; | |
ptr[1] = alpha; | |
} | |
} | |
else | |
{ | |
/* Non run-length packet */ | |
memcpy (ptr, data + j, size * sizeof (GLubyte) * 2); | |
ptr += size * 2; | |
j += size * 2; | |
} | |
} | |
} | |
static struct gl_texture_t * | |
ReadTGAFile (const char *filename) | |
{ | |
FILE *fp; | |
struct gl_texture_t *texinfo; | |
struct tga_header_t *header; | |
GLubyte *buffer = NULL; | |
GLubyte *colormap = NULL; | |
GLubyte *ptr; | |
long length; | |
/* Open file */ | |
fp = fopen (filename, "rb"); | |
if (!fp) | |
{ | |
fprintf (stderr, "error: couldn't open \"%s\"!\n", filename); | |
return NULL; | |
} | |
/* Get file length */ | |
fseek (fp, 0, SEEK_END); | |
length = ftell (fp); | |
fseek (fp, 0, SEEK_SET); | |
buffer = (GLubyte *) malloc (sizeof (GLubyte) * length); | |
if (!buffer) | |
{ | |
fprintf (stderr, "Error: memory allocation failed " | |
"for \"%s\"\n", filename); | |
fclose (fp); | |
return NULL; | |
} | |
/* Read whole file data */ | |
fread (buffer, sizeof (GLubyte), length, fp); | |
fclose (fp); | |
ptr = buffer; | |
/* Extract header */ | |
header = (struct tga_header_t *)ptr; | |
ptr += sizeof (struct tga_header_t); | |
texinfo = (struct gl_texture_t *)malloc (sizeof (struct gl_texture_t)); | |
GetTextureInfo (header, texinfo); | |
ptr += header->id_lenght; | |
/* Memory allocation for pixel data */ | |
texinfo->texels = (GLubyte *)malloc (sizeof (GLubyte) * | |
texinfo->width * texinfo->height * texinfo->internalFormat); | |
if (!texinfo->texels) | |
{ | |
free (texinfo); | |
return NULL; | |
} | |
/* Read color map */ | |
if (header->colormap_type) | |
{ | |
/* NOTE: color map is stored in BGR format */ | |
colormap = ptr; | |
ptr += header->cm_length * (header->cm_size >> 3); | |
} | |
/* Read image data */ | |
switch (header->image_type) | |
{ | |
case 0: | |
/* No data */ | |
break; | |
case 1: | |
/* Uncompressed 8 bits color index */ | |
ReadTGA8bits (ptr, colormap, texinfo); | |
break; | |
case 2: | |
/* Uncompressed 16-24-32 bits */ | |
switch (header->pixel_depth) | |
{ | |
case 16: | |
ReadTGA16bits (ptr, texinfo); | |
break; | |
case 24: | |
ReadTGA24bits (ptr, texinfo); | |
break; | |
case 32: | |
ReadTGA32bits (ptr, texinfo); | |
break; | |
} | |
break; | |
case 3: | |
/* Uncompressed 8 or 16 bits grayscale */ | |
if (header->pixel_depth == 8) | |
ReadTGAgray8bits (ptr, texinfo); | |
else /* 16 */ | |
ReadTGAgray16bits (ptr, texinfo); | |
break; | |
case 9: | |
/* RLE compressed 8 bits color index */ | |
ReadTGA8bitsRLE (ptr, colormap, texinfo); | |
break; | |
case 10: | |
/* RLE compressed 16-24-32 bits */ | |
switch (header->pixel_depth) | |
{ | |
case 16: | |
ReadTGA16bitsRLE (ptr, texinfo); | |
break; | |
case 24: | |
ReadTGA24bitsRLE (ptr, texinfo); | |
break; | |
case 32: | |
ReadTGA32bitsRLE (ptr, texinfo); | |
break; | |
} | |
break; | |
case 11: | |
/* RLE compressed 8 or 16 bits grayscale */ | |
if (header->pixel_depth == 8) | |
ReadTGAgray8bitsRLE (ptr, texinfo); | |
else /* 16 */ | |
ReadTGAgray16bitsRLE (ptr, texinfo); | |
break; | |
default: | |
/* Image type is not correct */ | |
fprintf (stderr, "error: unknown TGA image type %i!\n", | |
header->image_type); | |
free (texinfo->texels); | |
free (texinfo); | |
texinfo = NULL; | |
break; | |
} | |
if (buffer) | |
free (buffer); | |
return texinfo; | |
} | |
GLuint | |
loadTGATexture (const char *filename) | |
{ | |
struct gl_texture_t *tga_tex = NULL; | |
GLuint tex_id = 0; | |
GLint alignment; | |
tga_tex = ReadTGAFile (filename); | |
if (tga_tex && tga_tex->texels) | |
{ | |
/* Generate texture */ | |
glGenTextures (1, &tga_tex->id); | |
glBindTexture (GL_TEXTURE_2D, tga_tex->id); | |
/* Setup some parameters for texture filters and mipmapping */ | |
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR/*_MIPMAP_LINEAR*/); | |
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glGetIntegerv (GL_UNPACK_ALIGNMENT, &alignment); | |
glPixelStorei (GL_UNPACK_ALIGNMENT, 1); | |
#if 0 | |
glTexImage2D (GL_TEXTURE_2D, 0, tga_tex->internalFormat, | |
tga_tex->width, tga_tex->height, 0, tga_tex->format, | |
GL_UNSIGNED_BYTE, tga_tex->texels); | |
#else | |
gluBuild2DMipmaps (GL_TEXTURE_2D, tga_tex->internalFormat, | |
tga_tex->width, tga_tex->height, | |
tga_tex->format, GL_UNSIGNED_BYTE, tga_tex->texels); | |
#endif | |
glPixelStorei (GL_UNPACK_ALIGNMENT, alignment); | |
tex_id = tga_tex->id; | |
/* OpenGL has its own copy of texture data */ | |
free (tga_tex->texels); | |
free (tga_tex); | |
} | |
return tex_id; | |
} | |
static void | |
cleanup () | |
{ | |
glDeleteTextures (1, &texId); | |
} | |
static void | |
init (const char *filename) | |
{ | |
/* Initialize OpenGL */ | |
glClearColor (0.5f, 0.5f, 0.5f, 1.0f); | |
glShadeModel (GL_SMOOTH); | |
glEnable (GL_DEPTH_TEST); | |
glEnable (GL_BLEND); | |
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
/* Load TGA texture from file */ | |
texId = loadTGATexture (filename); | |
if (!texId) | |
exit (EXIT_FAILURE); | |
} | |
static void | |
reshape (int w, int h) | |
{ | |
if (h == 0) | |
h = 1; | |
glViewport (0, 0, (GLsizei)w, (GLsizei)h); | |
glMatrixMode (GL_PROJECTION); | |
glLoadIdentity (); | |
gluPerspective (45.0, w/(GLdouble)h, 0.1, 1000.0); | |
glMatrixMode (GL_MODELVIEW); | |
glLoadIdentity (); | |
glutPostRedisplay (); | |
} | |
static void | |
display () | |
{ | |
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
glLoadIdentity (); | |
glEnable (GL_TEXTURE_2D); | |
glBindTexture (GL_TEXTURE_2D, texId); | |
/* Draw textured quad */ | |
glTranslatef (0.0, 0.0, -5.0); | |
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 (); | |
glDisable (GL_TEXTURE_2D); | |
glutSwapBuffers (); | |
} | |
static void | |
keyboard (unsigned char key, int x, int y) | |
{ | |
/* Escape */ | |
if (key == 27) | |
exit (0); | |
} | |
int | |
main (int argc, char *argv[]) | |
{ | |
if (argc < 2) | |
{ | |
fprintf (stderr, "usage: %s <filename.tga>\n", argv[0]); | |
return -1; | |
} | |
glutInit (&argc, argv); | |
glutInitDisplayMode (GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); | |
glutInitWindowSize (640, 480); | |
glutCreateWindow ("TGA Texture Demo"); | |
atexit (cleanup); | |
init (argv[1]); | |
glutReshapeFunc (reshape); | |
glutDisplayFunc (display); | |
glutKeyboardFunc (keyboard); | |
glutMainLoop (); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* tga_r_mem.c -- tga texture loader (reverse height) | |
* last modification: apr. 6, 2009 | |
* | |
* Copyright (c) 2005-2009 David HENRY | |
* | |
* Permission is hereby granted, free of charge, to any person | |
* obtaining a copy of this software and associated documentation | |
* files (the "Software"), to deal in the Software without | |
* restriction, including without limitation the rights to use, | |
* copy, modify, merge, publish, distribute, sublicense, and/or | |
* sell copies of the Software, and to permit persons to whom the | |
* Software is furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be | |
* included in all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
* NONINFRINGEMENT. | |
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR | |
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
* | |
* gcc -Wall -ansi -lGL -lGLU -lglut tga_mem.c -o tga_mem | |
*/ | |
#include <GL/glut.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdint.h> | |
#include <stdbool.h> | |
/* File buffer struct */ | |
struct file_buffer_t | |
{ | |
char name[256]; | |
uint8_t *data; | |
long length; | |
long offset; | |
}; | |
/* OpenGL texture info */ | |
struct gl_texture_t | |
{ | |
GLsizei width; | |
GLsizei height; | |
GLenum format; | |
GLint internalFormat; | |
GLuint id; | |
GLubyte *texels; | |
}; | |
#pragma pack(push, 1) | |
/* TGA header */ | |
#if 1 | |
struct tga_header_t | |
{ | |
uint8_t id_lenght; /* size of image id */ | |
uint8_t colormap_type; /* 1 is has a colormap */ | |
/*uint8_t image_type;*/ /* compression type */ | |
uint8_t image_type : 3; /* 1 = color mapped, 2 = true color; 3 = grayscale */ | |
uint8_t compression : 1; /* 1 is rle encoding */ | |
uint8_t : 4; /* padding */ | |
uint16_t cm_first_entry; /* colormap origin */ | |
uint16_t cm_length; /* colormap length */ | |
uint8_t cm_size; /* colormap size */ | |
int16_t x_origin; /* bottom left x coord origin */ | |
int16_t y_origin; /* bottom left y coord origin */ | |
int16_t width; /* picture width (in pixels) */ | |
int16_t height; /* picture height (in pixels) */ | |
uint8_t pixel_depth; /* bits per pixel: 8, 16, 24 or 32 */ | |
uint8_t image_descriptor; /* 24 bits = 0x00; 32 bits = 0x80 */ | |
}; | |
#else | |
struct tga_header_t | |
{ | |
unsigned id_lenght : 8; /* size of image id */ | |
unsigned colormap_type : 8; /* 1 is has a colormap */ | |
unsigned image_type : 3; /* 1 = color mapped, 2 = true color; 3 = grayscale */ | |
unsigned compression : 1; /* 1 is rle encoding */ | |
unsigned : 4; /* padding */ | |
unsigned cm_first_entry : 16; /* colormap origin */ | |
unsigned cm_length : 16; /* colormap length */ | |
unsigned cm_size : 8; /* colormap size */ | |
unsigned x_origin : 16; /* bottom left x coord origin */ | |
unsigned y_origin : 16; /* bottom left y coord origin */ | |
unsigned width : 16; /* picture width (in pixels) */ | |
unsigned height : 16; /* picture height (in pixels) */ | |
unsigned pixel_depth : 8; /* bits per pixel: 8, 16, 24 or 32 */ | |
unsigned image_descriptor : 8; /* 24 bits = 0x00; 32 bits = 0x80 */ | |
}; | |
#endif | |
#pragma pack(pop) | |
/* TGA file reader structure */ | |
struct tga_file_t | |
{ | |
const struct tga_header_t *header; | |
const uint8_t *colormap; | |
const uint8_t *data; | |
long offset; | |
}; | |
#define TGA_COLOR_MAPPED 0x01 | |
#define TGA_TRUE_COLOR 0x02 | |
#define TGA_GRAYSCALE 0x03 | |
#define TGA_RLE_COMPRESSION 0x08 | |
/* Texture id for the demo */ | |
GLuint texId = 0; | |
static inline bool | |
tga_is_color_mapped (const struct tga_file_t *tga) | |
{ | |
return (tga->header->image_type == TGA_COLOR_MAPPED); | |
} | |
static inline bool | |
tga_is_true_color (const struct tga_file_t *tga) | |
{ | |
return (tga->header->image_type == TGA_TRUE_COLOR); | |
} | |
static inline bool | |
tga_is_grayscale (const struct tga_file_t *tga) | |
{ | |
return (tga->header->image_type == TGA_GRAYSCALE); | |
} | |
static inline bool | |
tga_is_compressed (const struct tga_file_t *tga) | |
{ | |
return tga->header->compression; | |
} | |
void | |
GetTextureInfo (const struct tga_header_t *header, | |
struct gl_texture_t *texinfo) | |
{ | |
texinfo->width = header->width; | |
texinfo->height = header->height; | |
switch (header->image_type) | |
{ | |
case TGA_COLOR_MAPPED: | |
case TGA_TRUE_COLOR: | |
{ | |
/* 8 bits and 16 bits images will be converted to 24 bits */ | |
if (header->pixel_depth <= 24) | |
{ | |
texinfo->format = GL_BGR; | |
texinfo->internalFormat = 3; | |
} | |
else /* 32 bits */ | |
{ | |
texinfo->format = GL_BGRA; | |
texinfo->internalFormat = 4; | |
} | |
break; | |
} | |
case TGA_GRAYSCALE: | |
{ | |
if (header->pixel_depth == 8) | |
{ | |
texinfo->format = GL_LUMINANCE; | |
texinfo->internalFormat = 1; | |
} | |
else /* 16 bits */ | |
{ | |
texinfo->format = GL_LUMINANCE_ALPHA; | |
texinfo->internalFormat = 2; | |
} | |
break; | |
} | |
} | |
} | |
void | |
tga_read_raw_packet (struct tga_file_t *tga, int size, | |
GLubyte *dest, int dest_depth) | |
{ | |
const uint8_t *src = &tga->data[tga->offset]; | |
GLubyte *ptr = dest; | |
int i, j; | |
if (tga_is_color_mapped (tga)) | |
{ | |
/* Convert to BGR 24 bits */ | |
for (i = 0; i < size; i++, ptr += 3) | |
memcpy (ptr, &tga->colormap[src[i] * 3], 3); | |
} | |
else if (tga_is_true_color (tga) && tga->header->pixel_depth == 16) | |
{ | |
for (i = 0, j = 0; i < size; i++, ptr += 3, j += 2) | |
{ | |
/* Read color word */ | |
uint16_t color = src[j] + (src[j + 1] << 8); | |
/* Convert to BGR 24 bits */ | |
ptr[0] = (GLubyte)(((color & 0x001F) >> 0) << 3); | |
ptr[1] = (GLubyte)(((color & 0x03E0) >> 5) << 3); | |
ptr[2] = (GLubyte)(((color & 0x7C00) >> 10) << 3); | |
} | |
} | |
else | |
{ | |
memcpy (ptr, src, size * dest_depth); | |
} | |
tga->offset += size * tga->header->pixel_depth / 8; | |
} | |
void | |
tga_read_rle_packet (struct tga_file_t *tga, int size, | |
GLubyte *dest, int dest_depth) | |
{ | |
const uint8_t *src = &tga->data[tga->offset]; | |
GLubyte *ptr = dest; | |
int i; | |
if (tga_is_color_mapped (tga)) | |
{ | |
/* Convert to BGR 24 bits */ | |
for (i = 0; i < size; i++, ptr += 3) | |
memcpy (ptr, &tga->colormap[src[0] * 3], 3); | |
} | |
else if (tga_is_true_color (tga) && | |
tga->header->pixel_depth == 16) | |
{ | |
/* Read color word */ | |
uint16_t color = src[0] + (src[1] << 8); | |
for (i = 0; i < size; i++, ptr += 3) | |
{ | |
/* Convert to BGR 24 bits */ | |
ptr[0] = (GLubyte)(((color & 0x001F) >> 0) << 3); | |
ptr[1] = (GLubyte)(((color & 0x03E0) >> 5) << 3); | |
ptr[2] = (GLubyte)(((color & 0x7C00) >> 10) << 3); | |
} | |
} | |
else | |
{ | |
for (i = 0; i < size; i++, ptr += dest_depth) | |
memcpy (ptr, src, dest_depth); | |
} | |
tga->offset += tga->header->pixel_depth / 8; | |
} | |
void | |
tga_read_scanline_rle (struct tga_file_t *tga, | |
GLubyte *dest, int dest_depth) | |
{ | |
int size; | |
GLubyte packet_header; | |
GLubyte *ptr = dest; | |
while (ptr < dest + tga->header->width * dest_depth) | |
{ | |
/* Read first byte */ | |
packet_header = tga->data[tga->offset++]; | |
size = 1 + (packet_header & 0x7f); | |
if (packet_header & 0x80) | |
{ | |
/* Run-length packet */ | |
tga_read_rle_packet (tga, size, ptr, dest_depth); | |
} | |
else | |
{ | |
/* Raw packet (non-run-length encoded) */ | |
tga_read_raw_packet (tga, size, ptr, dest_depth); | |
} | |
ptr += size * dest_depth; | |
} | |
} | |
void | |
tga_read_scanline_uncompressed (struct tga_file_t *tga, GLubyte *dest) | |
{ | |
tga_read_raw_packet (tga, tga->header->width, dest, | |
tga->header->pixel_depth / 8); | |
} | |
bool | |
tga_read_file (const struct file_buffer_t *buffer, | |
struct gl_texture_t *texinfo) | |
{ | |
struct tga_file_t tga; | |
GLubyte *line; | |
int i; | |
tga.data = buffer->data; | |
tga.offset = 0; | |
/* Extract header */ | |
tga.header = (struct tga_header_t *)tga.data; | |
tga.offset = sizeof (struct tga_header_t) + tga.header->id_lenght; | |
GetTextureInfo (tga.header, texinfo); | |
/* Read color map data */ | |
if (tga.header->colormap_type) | |
{ | |
/* NOTE: color map is stored in BGR format */ | |
tga.colormap = &tga.data[tga.offset]; | |
tga.offset += tga.header->cm_length * (tga.header->cm_size / 8); | |
} | |
/* No pixel data, but still a valid TGA image file */ | |
if (tga.header->image_type == 0) | |
return true; | |
/* Memory allocation for pixel data */ | |
texinfo->texels = (GLubyte *)malloc (sizeof (GLubyte) * | |
texinfo->width * texinfo->height * texinfo->internalFormat); | |
if (!texinfo->texels) | |
return false; | |
/* Read image data */ | |
for (i = 0; i < texinfo->height; i++) | |
{ | |
#if 0 | |
line = &texinfo->texels[texinfo->width * i * texinfo->internalFormat]; | |
#else | |
line = &texinfo->texels[(texinfo->height - (i + 1)) | |
* texinfo->width * texinfo->internalFormat]; | |
#endif | |
if (tga_is_compressed (&tga)) | |
tga_read_scanline_rle (&tga, line, texinfo->internalFormat); | |
else | |
tga_read_scanline_uncompressed (&tga, line); | |
} | |
return true; | |
} | |
bool | |
read_file (const char *filename, struct file_buffer_t *file) | |
{ | |
FILE *fp; | |
/* Open file */ | |
fp = fopen (filename, "rb"); | |
if (!fp) | |
{ | |
fprintf (stderr, "error: couldn't open \"%s\"!\n", filename); | |
return false; | |
} | |
/* Copy file name */ | |
strncpy (file->name, filename, sizeof (file->name)); | |
/* Get file length */ | |
fseek (fp, 0, SEEK_END); | |
file->length = ftell (fp); | |
file->offset = 0; | |
fseek (fp, 0, SEEK_SET); | |
/* Allocate memory for file data */ | |
file->data = (uint8_t *)malloc (file->length); | |
if (!file->data) | |
{ | |
fprintf (stderr, "Error: memory allocation failed " | |
"for \"%s\"\n", filename); | |
fclose (fp); | |
return false; | |
} | |
/* Read whole file data */ | |
fread (file->data, 1, file->length, fp); | |
fclose (fp); | |
return true; | |
} | |
GLuint | |
loadTGATexture (const char *filename) | |
{ | |
struct gl_texture_t tga_tex; | |
struct file_buffer_t buffer; | |
GLuint tex_id = 0; | |
GLint alignment; | |
if (!read_file (filename, &buffer)) | |
return 0; | |
if (tga_read_file (&buffer, &tga_tex)) | |
{ | |
glGenTextures (1, &tga_tex.id); | |
glBindTexture (GL_TEXTURE_2D, tga_tex.id); | |
glGetIntegerv (GL_UNPACK_ALIGNMENT, &alignment); | |
glPixelStorei (GL_UNPACK_ALIGNMENT, 1); | |
#if 0 | |
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glTexImage2D (GL_TEXTURE_2D, 0, tga_tex.internalFormat, | |
tga_tex.width, tga_tex.height, 0, tga_tex.format, | |
GL_UNSIGNED_BYTE, tga_tex.texels); | |
#else | |
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); | |
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
gluBuild2DMipmaps (GL_TEXTURE_2D, tga_tex.internalFormat, | |
tga_tex.width, tga_tex.height, | |
tga_tex.format, GL_UNSIGNED_BYTE, tga_tex.texels); | |
#endif | |
glPixelStorei (GL_UNPACK_ALIGNMENT, alignment); | |
tex_id = tga_tex.id; | |
} | |
if (tga_tex.texels) | |
free (tga_tex.texels); | |
free (buffer.data); | |
return tex_id; | |
} | |
static void | |
cleanup () | |
{ | |
glDeleteTextures (1, &texId); | |
} | |
static void | |
init (const char *filename) | |
{ | |
/* Initialize OpenGL */ | |
glClearColor (0.5f, 0.5f, 0.5f, 1.0f); | |
glShadeModel (GL_SMOOTH); | |
glEnable (GL_DEPTH_TEST); | |
glEnable (GL_BLEND); | |
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
/* Load TGA texture from file */ | |
texId = loadTGATexture (filename); | |
if (!texId) | |
exit (EXIT_FAILURE); | |
} | |
static void | |
reshape (int w, int h) | |
{ | |
if (h == 0) | |
h = 1; | |
glViewport (0, 0, (GLsizei)w, (GLsizei)h); | |
glMatrixMode (GL_PROJECTION); | |
glLoadIdentity (); | |
gluPerspective (45.0, w/(GLdouble)h, 0.1, 1000.0); | |
glMatrixMode (GL_MODELVIEW); | |
glLoadIdentity (); | |
glutPostRedisplay (); | |
} | |
static void | |
display () | |
{ | |
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
glLoadIdentity (); | |
glEnable (GL_TEXTURE_2D); | |
glBindTexture (GL_TEXTURE_2D, texId); | |
/* Draw textured quad */ | |
glTranslatef (0.0, 0.0, -5.0); | |
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 (); | |
glDisable (GL_TEXTURE_2D); | |
glutSwapBuffers (); | |
} | |
static void | |
keyboard (unsigned char key, int x, int y) | |
{ | |
/* Escape */ | |
if (key == 27) | |
exit (0); | |
} | |
int | |
main (int argc, char *argv[]) | |
{ | |
if (argc < 2) | |
{ | |
fprintf (stderr, "usage: %s <filename.tga>\n", argv[0]); | |
return -1; | |
} | |
glutInit (&argc, argv); | |
glutInitDisplayMode (GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); | |
glutInitWindowSize (640, 480); | |
glutCreateWindow ("TGA Texture Demo"); | |
atexit (cleanup); | |
init (argv[1]); | |
glutReshapeFunc (reshape); | |
glutDisplayFunc (display); | |
glutKeyboardFunc (keyboard); | |
glutMainLoop (); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment