savemii/src/tga_reader.c
2017-08-29 01:59:21 -03:00

577 lines
16 KiB
C

/**
* tga_reader.c
*
* Copyright (c) 2014 Kenji Sasaki
* Released under the MIT license.
* https://github.com/npedotnet/TGAReader/blob/master/LICENSE
*
* English document
* https://github.com/npedotnet/TGAReader/blob/master/README.md
*
* Japanese document
* http://3dtech.jp/wiki/index.php?TGAReader
*
*/
#include "tga_reader.h"
static const TGA_ORDER _TGA_READER_ARGB = {16, 8, 0, 24};
const TGA_ORDER *TGA_READER_ARGB = &_TGA_READER_ARGB;
static const TGA_ORDER _TGA_READER_ABGR = {0, 8, 16, 24};
const TGA_ORDER *TGA_READER_ABGR = &_TGA_READER_ABGR;
static const TGA_ORDER _TGA_READER_RGBA = {24, 16, 8, 0};
const TGA_ORDER *TGA_READER_RGBA = &_TGA_READER_RGBA;
void *tgaMalloc(size_t size) {
return malloc(size);
}
void tgaFree(void *memory) {
free(memory);
}
int tgaGetWidth(const unsigned char *buffer) {
return (buffer[12] & 0xFF) | (buffer[13] & 0xFF) << 8;
}
int tgaGetHeight(const unsigned char *buffer) {
return (buffer[14] & 0xFF) | (buffer[15] & 0xFF) << 8;
}
#define COLORMAP 1
#define RGB 2
#define GRAYSCALE 3
#define COLORMAP_RLE 9
#define RGB_RLE 10
#define GRAYSCALE_RLE 11
#define RIGHT_ORIGIN 0x10
#define UPPER_ORIGIN 0x20
static unsigned char *decodeRLE(int width, int height, int depth, const unsigned char *buffer, int offset);
static int *createPixelsFromColormap(int width, int height, int depth, const unsigned char *bytes, int offset, const unsigned char *palette, int colormapOrigin, int descriptor, const TGA_ORDER *order);
static int *createPixelsFromRGB(int width, int height, int depth, const unsigned char *bytes, int offset, int descriptor, const TGA_ORDER *order);
static int *createPixelsFromGrayscale(int width, int height, int depth, const unsigned char *bytes, int offset, int descriptor, const TGA_ORDER *order);
int *tgaRead(const unsigned char *buffer, const TGA_ORDER *order) {
// header
// int idFieldLength = buffer[0] & 0xFF;
// int colormapType = buffer[1] & 0xFF;
int type = buffer[2] & 0xFF;
int colormapOrigin = (buffer[3] & 0xFF) | (buffer[4] & 0xFF) << 8;
int colormapLength = (buffer[5] & 0xFF) | (buffer[6] & 0xFF) << 8;
int colormapDepth = buffer[7] & 0xFF;
// int originX = (buffer[8] & 0xFF) | (buffer[9] & 0xFF) << 8; // unsupported
// int originY = (buffer[10] & 0xFF) | (buffer[11] & 0xFF) << 8; // unsupported
int width = tgaGetWidth(buffer);
int height = tgaGetHeight(buffer);
int depth = buffer[16] & 0xFF;
int descriptor = buffer[17] & 0xFF;
int *pixels = NULL;
// data
switch(type) {
case COLORMAP: {
int imageDataOffset = 18 + (colormapDepth / 8) * colormapLength;
pixels = createPixelsFromColormap(width, height, colormapDepth, buffer, imageDataOffset, buffer, colormapOrigin, descriptor, order);
} break;
case RGB:
pixels = createPixelsFromRGB(width, height, depth, buffer, 18, descriptor, order);
break;
case GRAYSCALE:
pixels = createPixelsFromGrayscale(width, height, depth, buffer, 18, descriptor, order);
break;
case COLORMAP_RLE: {
int imageDataOffset = 18 + (colormapDepth / 8) * colormapLength;
unsigned char *decodeBuffer = decodeRLE(width, height, depth, buffer, imageDataOffset);
pixels = createPixelsFromColormap(width, height, colormapDepth, decodeBuffer, 0, buffer, colormapOrigin, descriptor, order);
tgaFree(decodeBuffer);
} break;
case RGB_RLE: {
unsigned char *decodeBuffer = decodeRLE(width, height, depth, buffer, 18);
pixels = createPixelsFromRGB(width, height, depth, decodeBuffer, 0, descriptor, order);
tgaFree(decodeBuffer);
} break;
case GRAYSCALE_RLE: {
unsigned char *decodeBuffer = decodeRLE(width, height, depth, buffer, 18);
pixels = createPixelsFromGrayscale(width, height, depth, decodeBuffer, 0, descriptor, order);
tgaFree(decodeBuffer);
} break;
default:
break;
}
return pixels;
}
static unsigned char *decodeRLE(int width, int height, int depth, const unsigned char *buffer, int offset) {
int elementCount = depth/8;
unsigned char elements[4];
int decodeBufferLength = elementCount * width * height;
unsigned char *decodeBuffer = (unsigned char *)tgaMalloc(decodeBufferLength);
int decoded = 0;
while(decoded < decodeBufferLength) {
int packet = buffer[offset++] & 0xFF;
if((packet & 0x80) != 0) { // RLE
int i, j, count;
for(i=0; i<elementCount; i++) {
elements[i] = buffer[offset++];
}
count = (packet&0x7F)+1;
for(i=0; i<count; i++) {
for(j=0; j<elementCount; j++) {
decodeBuffer[decoded++] = elements[j];
}
}
}
else { // RAW
int count = (packet+1) * elementCount;
int i;
for(i=0; i<count; i++) {
decodeBuffer[decoded++] = buffer[offset++];
}
}
}
return decodeBuffer;
}
static int *createPixelsFromColormap(int width, int height, int depth, const unsigned char *bytes, int offset, const unsigned char *palette, int colormapOrigin, int descriptor, const TGA_ORDER *order) {
int *pixels = NULL;
int rs = order->redShift;
int gs = order->greenShift;
int bs = order->blueShift;
int as = order->alphaShift;
switch(depth) {
case 24:
pixels = (int *)tgaMalloc(4*width*height);
if((descriptor & RIGHT_ORIGIN) != 0) {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = bytes[offset+width*i+j] & 0xFF - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 3*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*i+(width-j-1)] = color;
}
}
}
else {
// LowerRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = bytes[offset+width*i+j] & 0xFF - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 3*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*(height-i-1)+(width-j-1)] = color;
}
}
}
}
else {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = bytes[offset+width*i+j] & 0xFF - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 3*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*i+j] = color;
}
}
}
else {
// LowerLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = bytes[offset+width*i+j] & 0xFF - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 3*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*(height-i-1)+j] = color;
}
}
}
}
break;
case 32:
pixels = (int *)tgaMalloc(4*width*height);
if((descriptor & RIGHT_ORIGIN) != 0) {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = bytes[offset+width*i+j] & 0xFF - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 4*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = palette[index+3] & 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*i+(width-j-1)] = color;
}
}
}
else {
// LowerRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = bytes[offset+width*i+j] & 0xFF - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 4*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = palette[index+3] & 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*(height-i-1)+(width-j-1)] = color;
}
}
}
}
else {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = bytes[offset+width*i+j] & 0xFF - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 4*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = palette[index+3] & 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*i+j] = color;
}
}
}
else {
// LowerLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = bytes[offset+width*i+j] & 0xFF - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 4*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = palette[index+3] & 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*(height-i-1)+j] = color;
}
}
}
}
break;
default:
break;
}
return pixels;
}
static int *createPixelsFromRGB(int width, int height, int depth, const unsigned char *bytes, int offset, int descriptor, const TGA_ORDER *order) {
int *pixels = NULL;
int rs = order->redShift;
int gs = order->greenShift;
int bs = order->blueShift;
int as = order->alphaShift;
switch(depth) {
case 24:
pixels = (int *)tgaMalloc(4*width*height);
if((descriptor & RIGHT_ORIGIN) != 0) {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+3*width*i+3*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = 0xFF;
pixels[width*i+(width-j-1)] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
else {
// LowerRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+3*width*i+3*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = 0xFF;
pixels[width*(height-i-1)+(width-j-1)] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
}
else {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+3*width*i+3*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = 0xFF;
pixels[width*i+j] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
else {
// LowerLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+3*width*i+3*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = 0xFF;
pixels[width*(height-i-1)+j] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
}
break;
case 32:
pixels = (int *)tgaMalloc(4*width*height);
if((descriptor & RIGHT_ORIGIN) != 0) {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+4*width*i+4*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = bytes[index+3] & 0xFF;
pixels[width*i+(width-j-1)] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
else {
// LowerRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+4*width*i+4*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = bytes[index+3] & 0xFF;
pixels[width*(height-i-1)+(width-j-1)] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
}
else {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+4*width*i+4*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = bytes[index+3] & 0xFF;
pixels[width*i+j] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
else {
// LowerLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+4*width*i+4*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = bytes[index+3] & 0xFF;
pixels[width*(height-i-1)+j] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
}
break;
default:
break;
}
return pixels;
}
static int *createPixelsFromGrayscale(int width, int height, int depth, const unsigned char *bytes, int offset, int descriptor, const TGA_ORDER *order) {
int *pixels = NULL;
int rs = order->redShift;
int gs = order->greenShift;
int bs = order->blueShift;
int as = order->alphaShift;
switch(depth) {
case 8:
pixels = (int *)tgaMalloc(4*width*height);
if((descriptor & RIGHT_ORIGIN) != 0) {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+width*i+j] & 0xFF;
int a = 0xFF;
pixels[width*i+(width-j-1)] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
else {
// LowerRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+width*i+j] & 0xFF;
int a = 0xFF;
pixels[width*(height-i-1)+(width-j-1)] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
}
else {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+width*i+j] & 0xFF;
int a = 0xFF;
pixels[width*i+j] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
else {
// LowerLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+width*i+j] & 0xFF;
int a = 0xFF;
pixels[width*(height-i-1)+j] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
}
break;
case 16:
pixels = (int *)tgaMalloc(4*width*height);
if((descriptor & RIGHT_ORIGIN) != 0) {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+2*width*i+2*j+0] & 0xFF;
int a = bytes[offset+2*width*i+2*j+1] & 0xFF;
pixels[width*i+(width-j-1)] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
else {
// LowerRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+2*width*i+2*j+0] & 0xFF;
int a = bytes[offset+2*width*i+2*j+1] & 0xFF;
pixels[width*(height-i-1)+(width-j-1)] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
}
else {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+2*width*i+2*j+0] & 0xFF;
int a = bytes[offset+2*width*i+2*j+1] & 0xFF;
pixels[width*i+j] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
else {
// LowerLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+2*width*i+2*j+0] & 0xFF;
int a = bytes[offset+2*width*i+2*j+1] & 0xFF;
pixels[width*(height-i-1)+j] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
}
break;
default:
break;
}
return pixels;
}
/* EOF */