FasTC/IO/third_party/tga/targa.c
2013-12-02 12:52:44 -05:00

648 lines
15 KiB
C
Executable file

/*
* targa.c
*
* Copyright (C) 2006 - 2009 by Joshua S. English.
*
* This software is the intellectual property of Joshua S. English. This
* software is provided 'as-is', without any express or implied warranty. In no
* event will the author be held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source version must be plainly marked as such, and must not be
* misrepresented as being original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
*
* Notes:
*
* A plugin to read targa (TGA) image files into an OpenGL-compatible RGBA
* format.
*
* Written by Joshua S. English.
*/
// preprocessor directives
#ifdef _MSC_VER
# define WIN32_LEAN_AND_MEAN
# define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "targa.h"
// define targa private constants
#define TGA_COLOR_MAP_NONE 0
#define TGA_IMAGE_TYPE_NONE 0
#define TGA_IMAGE_TYPE_CM 1
#define TGA_IMAGE_TYPE_BGR 2
#define TGA_IMAGE_TYPE_BW 3
#define TGA_IMAGE_TYPE_RLE_CM 9
#define TGA_IMAGE_TYPE_RLE_BGR 10
#define TGA_IMAGE_TYPE_RLE_BW 11
#define TGA_R 0
#define TGA_G 1
#define TGA_B 2
#define TGA_A 3
// declare targa private functions
static int handleTargaError(FILE *fh, int errorCode, const char *function,
size_t line);
static int ctoi(char value);
static char *localStrndup(char *string, int length);
// define targa private macros
#define targaErrorf() \
handleTargaError(fh, rc, __FUNCTION__, __LINE__)
// define targa private functions
static int handleTargaError(FILE *fh, int errorCode, const char *function,
size_t line)
{
char *errorMessage = NULL;
if((errorMessage = strerror(errno)) == NULL) {
errorMessage = "uknown error";
}
fprintf(stderr, "[%s():%i] error(%i) - #%i, '%s'.\n",
(char *)function, (int)line, errorCode, (int)errno, errorMessage);
if(fh != NULL) {
fclose(fh);
}
return -1;
}
static int ctoi(char value)
{
return (int)((unsigned char)value);
}
static char *localStrndup(char *string, int length)
{
char *result = NULL;
if((string == NULL) || (length < 1)) {
fprintf(stderr, "[%s():%i] error - invalid or missing argument(s).\n",
__FUNCTION__, __LINE__);
return NULL;
}
#if defined(_GNU_SOURCE)
result = strndup(string, length);
#else // !_GNU_SOURCE
result = (char *)malloc(sizeof(char) * (length + 1));
memset(result, 0, (sizeof(char) * (length + 1)));
memcpy(result, string, length);
#endif // _GNU_SOURCE
return result;
}
// define targa public functions
int targa_init(Targa *targa)
{
if(targa == NULL) {
fprintf(stderr, "[%s():%i] error - invalid or missing argument(s).\n",
__FUNCTION__, __LINE__);
return -1;
}
memset((void *)targa, 0, sizeof(Targa));
targa->width = 0;
targa->height = 0;
targa->imageLength = 0;
targa->image = NULL;
return 0;
}
int targa_free(Targa *targa)
{
if(targa == NULL) {
fprintf(stderr, "[%s():%i] error - invalid or missing argument(s).\n",
__FUNCTION__, __LINE__);
return -1;
}
if(targa->image != NULL) {
free(targa->image);
}
memset((void *)targa, 0, sizeof(Targa));
return 0;
}
int targa_getDimensions(Targa *targa, int *width, int *height)
{
if((targa == NULL) || (width == NULL) || (height == NULL)) {
fprintf(stderr, "[%s():%i] error - invalid or missing argument(s).\n",
__FUNCTION__, __LINE__);
return -1;
}
*width = targa->width;
*height = targa->height;
return 0;
}
int targa_getImageLength(Targa *targa, int *imageLength)
{
if((targa == NULL) || (imageLength == NULL)) {
fprintf(stderr, "[%s():%i] error - invalid or missing argument(s).\n",
__FUNCTION__, __LINE__);
return -1;
}
*imageLength = targa->imageLength;
return 0;
}
int targa_getRgbaTexture(Targa *targa, char **texture, int *textureLength)
{
if((targa == NULL) || (texture == NULL) || (textureLength == NULL)) {
fprintf(stderr, "[%s():%i] error - invalid or missing argument(s).\n",
__FUNCTION__, __LINE__);
return -1;
}
*texture = (char *)targa->image;
*textureLength = targa->imageLength;
return 0;
}
int targa_loadFromFile(Targa *targa, char *filename)
{
int rc = 0;
int fileLength = 0;
unsigned char *buffer = NULL;
FILE *fh = NULL;
if((targa == NULL) || (filename == NULL)) {
fprintf(stderr, "[%s():%i] error - invalid or missing argument(s).\n",
__FUNCTION__, __LINE__);
return -1;
}
if((fh = fopen(filename, "r+b")) == NULL) {
return targaErrorf();
}
if((rc = fseek(fh, 0, SEEK_END)) != 0) {
return targaErrorf();
}
if((fileLength = ftell(fh)) < 0) {
return targaErrorf();
}
if((rc = fseek(fh, 0, SEEK_SET)) != 0) {
return targaErrorf();
}
if(fileLength < 18) {
fprintf(stderr, "error - TGA file '%s' length %i invalid.\n",
filename, fileLength);
fclose(fh);
return -1;
}
buffer = (unsigned char *)malloc(sizeof(unsigned char) * fileLength);
memset(buffer, 0, (sizeof(unsigned char) * fileLength));
rc = (int)fread((char *)buffer, sizeof(char), fileLength, fh);
if(rc != fileLength) {
return targaErrorf();
}
fclose(fh);
rc = targa_loadFromData(targa, buffer, fileLength);
free(buffer);
return rc;
}
int targa_loadFromData(Targa *targa, const unsigned char *data, int dataLength)
{
short sNumber = 0;
int ii = 0;
int nn = 0;
int imageIdLength = 0;
int colorMap = 0;
int imageType = 0;
int bitLength = 0;
int colorMode = 0;
int length = 0;
int rleId = 0;
int pixel[4];
unsigned char *imageId = NULL;
const unsigned char *ptr = NULL;
if((targa == NULL) || (data == NULL) || (dataLength < 18)) {
fprintf(stderr, "[%s():%i] error - invalid or missing argument(s).\n",
__FUNCTION__, __LINE__);
return -1;
}
ptr = data;
// determine image ID length
imageIdLength = (int)ptr[0];
ptr++;
if((int)(ptr - data) > dataLength) {
fprintf(stderr, "[%s():%i] error - detected data overrun with %i vs "
"%i.\n", __FUNCTION__, __LINE__, (int)(ptr - data),
dataLength);
return -1;
}
// check for color map
colorMap = (int)ptr[0];
ptr++;
if((int)(ptr - data) > dataLength) {
fprintf(stderr, "[%s():%i] error - detected data overrun with %i vs "
"%i.\n", __FUNCTION__, __LINE__, (int)(ptr - data),
dataLength);
return -1;
}
if(colorMap != TGA_COLOR_MAP_NONE) {
fprintf(stderr, "[%s():%i] error - unable to read TARGA color map "
"%i.\n", __FUNCTION__, __LINE__, colorMap);
return -1;
}
// obtain image type
imageType = (int)ptr[0];
ptr++;
if((int)(ptr - data) > dataLength) {
fprintf(stderr, "[%s():%i] error - detected data overrun with %i vs "
"%i.\n", __FUNCTION__, __LINE__, (int)(ptr - data),
dataLength);
return -1;
}
if((imageType == TGA_IMAGE_TYPE_NONE) ||
(imageType == TGA_IMAGE_TYPE_CM) ||
(imageType == TGA_IMAGE_TYPE_RLE_CM)) {
fprintf(stderr, "[%s():%i] error - unsupported image type %i.\n",
__FUNCTION__, __LINE__, imageType);
return -1;
}
// skip 9 bytes (color-map information and x & y origins)
ptr += 9;
if((int)(ptr - data) > dataLength) {
fprintf(stderr, "[%s():%i] error - detected data overrun with %i vs "
"%i.\n", __FUNCTION__, __LINE__, (int)(ptr - data),
dataLength);
return -1;
}
// obtain image width
memcpy((char *)&sNumber, ptr, sizeof(short));
ptr += sizeof(short);
if((int)(ptr - data) > dataLength) {
fprintf(stderr, "[%s():%i] error - detected data overrun with %i vs "
"%i.\n", __FUNCTION__, __LINE__, (int)(ptr - data),
dataLength);
return -1;
}
if(sNumber < 1) {
fprintf(stderr, "[%s():%i] error - invalid image width %i.\n",
__FUNCTION__, __LINE__, (int)sNumber);
return -1;
}
targa->width = (int)sNumber;
// obtain image height
memcpy((char *)&sNumber, ptr, sizeof(short));
ptr += sizeof(short);
if((int)(ptr - data) > dataLength) {
fprintf(stderr, "[%s():%i] error - detected data overrun with %i vs "
"%i.\n", __FUNCTION__, __LINE__, (int)(ptr - data),
dataLength);
return -1;
}
if(sNumber < 1) {
fprintf(stderr, "[%s():%i] error - invalid image height %i.\n",
__FUNCTION__, __LINE__, (int)sNumber);
return -1;
}
targa->height = (int)sNumber;
// determine pixel depth
bitLength = (int)ptr[0];
ptr++;
if((int)(ptr - data) > dataLength) {
fprintf(stderr, "[%s():%i] error - detected data overrun with %i vs "
"%i.\n", __FUNCTION__, __LINE__, (int)(ptr - data),
dataLength);
return -1;
}
if((bitLength != 16) && (bitLength != 24) && (bitLength != 32)) {
fprintf(stderr, "[%s():%i] error - unknown pixel depth of %i-bits.\n",
__FUNCTION__, __LINE__, bitLength);
return -1;
}
if((bitLength == 16) &&
((imageType != TGA_IMAGE_TYPE_BGR) &&
(imageType != TGA_IMAGE_TYPE_BW))) {
fprintf(stderr, "[%s():%i] error - unable to RLE-decode pixel depth "
"of %i-bits.\n", __FUNCTION__, __LINE__, bitLength);
return -1;
}
// skip 1 byte (image descriptor)
ptr++;
if((int)(ptr - data) > dataLength) {
fprintf(stderr, "[%s():%i] error - detected data overrun with %i vs "
"%i.\n", __FUNCTION__, __LINE__, (int)(ptr - data),
dataLength);
return -1;
}
// obtain the image ID
if(imageIdLength > 0) {
if(((int)(ptr - data) + imageIdLength) > dataLength) {
fprintf(stderr, "[%s():%i] error - detected data overrun at %i "
"(image ID) by %i bytes.\n", __FUNCTION__, __LINE__,
(int)(ptr - data),
(((int)(ptr - data) + imageIdLength) - dataLength));
return -1;
}
imageId = (unsigned char *)localStrndup((char *)ptr, imageIdLength);
ptr += imageIdLength;
if((int)(ptr - data) > dataLength) {
fprintf(stderr, "[%s():%i] error - detected data overrun with %i "
"vs %i.\n", __FUNCTION__, __LINE__, (int)(ptr - data),
dataLength);
return -1;
}
}
// process the image
targa->imageLength = (long int)(targa->width * targa->height * 4);
targa->image = (unsigned char *)malloc(sizeof(unsigned char) *
targa->imageLength);
if((imageType == TGA_IMAGE_TYPE_BGR) || (imageType == TGA_IMAGE_TYPE_BW)) {
if(bitLength == 16) {
colorMode = 2;
}
else {
colorMode = (bitLength / 8);
}
length = (targa->width * targa->height * colorMode);
if(((int)(ptr - data) + length) > dataLength) {
fprintf(stderr, "[%s():%i] error - detected data overrun at %i "
"(image pixels) by %i bytes.\n", __FUNCTION__, __LINE__,
(int)(ptr - data),
(((int)(ptr - data) + length) - dataLength));
return -1;
}
for(ii = 0, nn = 0; ((ii < length) && (nn < targa->imageLength));
ii += colorMode, nn += 4) {
if(colorMode == 2) {
memcpy((char *)&sNumber, ptr, sizeof(short));
pixel[TGA_R] = ctoi((sNumber & 0x1f) << 3);
pixel[TGA_G] = ctoi(((sNumber >> 5) & 0x1f) << 3);
pixel[TGA_B] = ctoi(((sNumber >> 10) & 0x1f) << 3);
pixel[TGA_A] = 255;
}
else {
pixel[TGA_R] = ctoi(ptr[2]);
pixel[TGA_G] = ctoi(ptr[1]);
pixel[TGA_B] = ctoi(ptr[0]);
if(colorMode == 3) {
pixel[TGA_A] = 255;
}
else {
pixel[TGA_A] = ctoi(ptr[3]);
}
}
targa->image[(nn + 0)] = (unsigned char)pixel[TGA_R];
targa->image[(nn + 1)] = (unsigned char)pixel[TGA_G];
targa->image[(nn + 2)] = (unsigned char)pixel[TGA_B];
targa->image[(nn + 3)] = (unsigned char)pixel[TGA_A];
ptr += colorMode;
}
}
else { // RLE image
ii = 0;
nn = 0;
rleId = 0;
colorMode = (bitLength / 8);
length = (targa->width * targa->height);
while(ii < length) {
rleId = (int)ptr[0];
ptr++;
if((int)(ptr - data) > dataLength) {
fprintf(stderr, "[%s():%i] error - detected data overrun with "
"%i vs %i.\n", __FUNCTION__, __LINE__,
(int)(ptr - data), dataLength);
return -1;
}
if(rleId < 128) {
rleId++;
while(rleId > 0) {
pixel[TGA_R] = ctoi(ptr[2]);
pixel[TGA_G] = ctoi(ptr[1]);
pixel[TGA_B] = ctoi(ptr[0]);
if(colorMode == 3) {
pixel[TGA_A] = 255;
}
else {
pixel[TGA_A] = ctoi(ptr[3]);
}
targa->image[(nn + 0)] = (unsigned char)pixel[TGA_R];
targa->image[(nn + 1)] = (unsigned char)pixel[TGA_G];
targa->image[(nn + 2)] = (unsigned char)pixel[TGA_B];
targa->image[(nn + 3)] = (unsigned char)pixel[TGA_A];
rleId--;
ii++;
nn += 4;
if(nn >= targa->imageLength) {
break;
}
ptr += colorMode;
if((int)(ptr - data) > dataLength) {
fprintf(stderr, "[%s():%i] error - detected data "
"overrun with %i vs %i.\n", __FUNCTION__,
__LINE__, (int)(ptr - data), dataLength);
return -1;
}
}
}
else {
pixel[TGA_R] = ctoi(ptr[2]);
pixel[TGA_G] = ctoi(ptr[1]);
pixel[TGA_B] = ctoi(ptr[0]);
if(colorMode == 3) {
pixel[TGA_A] = 255;
}
else {
pixel[TGA_A] = ctoi(ptr[3]);
}
ptr += colorMode;
if((int)(ptr - data) > dataLength) {
fprintf(stderr, "[%s():%i] error - detected data overrun "
"with %i vs %i.\n", __FUNCTION__, __LINE__,
(int)(ptr - data), dataLength);
return -1;
}
rleId -= 127;
while(rleId > 0) {
targa->image[(nn + 0)] = (unsigned char)pixel[TGA_R];
targa->image[(nn + 1)] = (unsigned char)pixel[TGA_G];
targa->image[(nn + 2)] = (unsigned char)pixel[TGA_B];
targa->image[(nn + 3)] = (unsigned char)pixel[TGA_A];
rleId--;
ii++;
nn += 4;
if(nn >= targa->imageLength) {
break;
}
}
}
if(nn >= targa->imageLength) {
break;
}
}
}
if(imageId != NULL) {
free(imageId);
}
return 0;
}
int targa_applyRgbaMask(Targa *targa, int colorType, unsigned char value)
{
int ii = 0;
int startPosition = 0;
if((targa == NULL) ||
((colorType != TARGA_COLOR_RED) &&
(colorType != TARGA_COLOR_GREEN) &&
(colorType != TARGA_COLOR_BLUE) &&
(colorType != TARGA_COLOR_ALPHA))) {
fprintf(stderr, "[%s():%i] error - invalid or missing argument(s).\n",
__FUNCTION__, __LINE__);
return -1;
}
switch(colorType) {
case TARGA_COLOR_RED:
startPosition = 0;
break;
case TARGA_COLOR_GREEN:
startPosition = 1;
break;
case TARGA_COLOR_BLUE:
startPosition = 2;
break;
case TARGA_COLOR_ALPHA:
startPosition = 3;
break;
}
for(ii = startPosition; ii < targa->imageLength; ii += 4) {
targa->image[ii] += value;
}
return 0;
}
int targa_setRgbaChannel(Targa *targa, int colorType, unsigned char value)
{
int ii = 0;
int startPosition = 0;
if((targa == NULL) ||
((colorType != TARGA_COLOR_RED) &&
(colorType != TARGA_COLOR_GREEN) &&
(colorType != TARGA_COLOR_BLUE) &&
(colorType != TARGA_COLOR_ALPHA))) {
fprintf(stderr, "[%s():%i] error - invalid or missing argument(s).\n",
__FUNCTION__, __LINE__);
return -1;
}
switch(colorType) {
case TARGA_COLOR_RED:
startPosition = 0;
break;
case TARGA_COLOR_GREEN:
startPosition = 1;
break;
case TARGA_COLOR_BLUE:
startPosition = 2;
break;
case TARGA_COLOR_ALPHA:
startPosition = 3;
break;
}
for(ii = startPosition; ii < targa->imageLength; ii += 4) {
targa->image[ii] = value;
}
return 0;
}