331 lines
6.9 KiB
C
331 lines
6.9 KiB
C
/*
|
|
* tgaread.c
|
|
*
|
|
* Copying (C) 2001-2002 Matthias Brueckner <matbrc@gmx.de>
|
|
* This file is part of the struct TGA library (libtga).
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "tga.h"
|
|
|
|
|
|
size_t
|
|
TGARead(TGA *tga,
|
|
tbyte *buf,
|
|
size_t size,
|
|
size_t n)
|
|
{
|
|
size_t read = fread(buf, size, n, tga->fd);
|
|
tga->off = ftell(tga->fd);
|
|
return read;
|
|
}
|
|
|
|
int
|
|
TGAReadImage(TGA *tga,
|
|
TGAData *data)
|
|
{
|
|
if (!tga) return 0;
|
|
|
|
if (TGAReadHeader(tga) != TGA_OK) {
|
|
TGA_ERROR(tga, tga->last);
|
|
return 0;
|
|
}
|
|
|
|
if ((data->flags & TGA_IMAGE_ID) && tga->hdr.id_len != 0) {
|
|
if (TGAReadImageId(tga, &data->img_id) != TGA_OK) {
|
|
data->flags &= ~TGA_IMAGE_ID;
|
|
TGA_ERROR(tga, tga->last);
|
|
}
|
|
} else {
|
|
data->flags &= ~TGA_IMAGE_ID;
|
|
}
|
|
|
|
if (data->flags & TGA_IMAGE_DATA) {
|
|
if (TGA_IS_MAPPED(tga)) {
|
|
if (!TGAReadColorMap(tga, &data->cmap, data->flags)) {
|
|
data->flags &= ~TGA_COLOR_MAP;
|
|
TGA_ERROR(tga, tga->last);
|
|
return 0;
|
|
} else {
|
|
data->flags |= TGA_COLOR_MAP;
|
|
}
|
|
}
|
|
|
|
data->img_data = (tbyte*)malloc(TGA_IMG_DATA_SIZE(tga));
|
|
if (!data->img_data) {
|
|
data->flags &= ~TGA_IMAGE_DATA;
|
|
TGA_ERROR(tga, TGA_OOM);
|
|
return 0;
|
|
}
|
|
|
|
if (TGAReadScanlines(tga, data->img_data, 0, tga->hdr.height, data->flags) != tga->hdr.height) {
|
|
data->flags &= ~TGA_IMAGE_DATA;
|
|
TGA_ERROR(tga, tga->last);
|
|
}
|
|
}
|
|
tga->last = TGA_OK;
|
|
return TGA_OK;
|
|
}
|
|
|
|
int
|
|
TGAReadHeader (TGA *tga)
|
|
{
|
|
tbyte *tmp;
|
|
|
|
if (!tga) return 0;
|
|
|
|
__TGASeek(tga, 0, SEEK_SET);
|
|
if (tga->off != 0) {
|
|
TGA_ERROR(tga, TGA_SEEK_FAIL);
|
|
return 0;
|
|
}
|
|
|
|
tmp = (tbyte*)malloc(TGA_HEADER_SIZE);
|
|
if (!tmp) {
|
|
TGA_ERROR(tga, TGA_OOM);
|
|
return 0;
|
|
}
|
|
|
|
memset(tmp, 0, TGA_HEADER_SIZE);
|
|
|
|
if (!TGARead(tga, tmp, TGA_HEADER_SIZE, 1)) {
|
|
free(tmp);
|
|
TGA_ERROR(tga, TGA_READ_FAIL);
|
|
return 0;
|
|
}
|
|
|
|
tga->hdr.id_len = tmp[0];
|
|
tga->hdr.map_t = tmp[1];
|
|
tga->hdr.img_t = tmp[2];
|
|
tga->hdr.map_first = tmp[3] + tmp[4] * 256;
|
|
tga->hdr.map_len = tmp[5] + tmp[6] * 256;
|
|
tga->hdr.map_entry = tmp[7];
|
|
tga->hdr.x = tmp[8] + tmp[9] * 256;
|
|
tga->hdr.y = tmp[10] + tmp[11] * 256;
|
|
tga->hdr.width = tmp[12] + tmp[13] * 256;
|
|
tga->hdr.height = tmp[14] + tmp[15] * 256;
|
|
tga->hdr.depth = tmp[16];
|
|
tga->hdr.alpha = tmp[17] & 0x0f;
|
|
tga->hdr.horz = (tmp[17] & 0x10) ? TGA_TOP : TGA_BOTTOM;
|
|
tga->hdr.vert = (tmp[17] & 0x20) ? TGA_RIGHT : TGA_LEFT;
|
|
|
|
if (tga->hdr.map_t && tga->hdr.depth != 8) {
|
|
TGA_ERROR(tga, TGA_UNKNOWN_SUB_FORMAT);
|
|
free(tga);
|
|
free(tmp);
|
|
return 0;
|
|
}
|
|
|
|
if (tga->hdr.depth != 8 &&
|
|
tga->hdr.depth != 15 &&
|
|
tga->hdr.depth != 16 &&
|
|
tga->hdr.depth != 24 &&
|
|
tga->hdr.depth != 32)
|
|
{
|
|
TGA_ERROR(tga, TGA_UNKNOWN_SUB_FORMAT);
|
|
free(tga);
|
|
free(tmp);
|
|
return 0;
|
|
}
|
|
|
|
free(tmp);
|
|
tga->last = TGA_OK;
|
|
return TGA_OK;
|
|
}
|
|
|
|
int
|
|
TGAReadImageId(TGA *tga,
|
|
tbyte **buf)
|
|
{
|
|
if (!tga || tga->hdr.id_len == 0) return 0;
|
|
|
|
__TGASeek(tga, TGA_HEADER_SIZE, SEEK_SET);
|
|
if (tga->off != TGA_HEADER_SIZE) {
|
|
TGA_ERROR(tga, TGA_SEEK_FAIL);
|
|
return 0;
|
|
}
|
|
*buf = (tbyte*)malloc(tga->hdr.id_len);
|
|
if (!buf) {
|
|
TGA_ERROR(tga, TGA_OOM);
|
|
return 0;
|
|
}
|
|
|
|
if (!TGARead(tga, *buf, tga->hdr.id_len, 1)) {
|
|
free(buf);
|
|
TGA_ERROR(tga, TGA_READ_FAIL);
|
|
return 0;
|
|
}
|
|
|
|
tga->last = TGA_OK;
|
|
return TGA_OK;
|
|
}
|
|
|
|
int
|
|
TGAReadColorMap (TGA *tga,
|
|
tbyte **buf,
|
|
tuint32 flags)
|
|
{
|
|
tlong i, n, off, read, tmp;
|
|
|
|
if (!tga) return 0;
|
|
|
|
n = TGA_CMAP_SIZE(tga);
|
|
if (n <= 0) return 0;
|
|
|
|
off = TGA_CMAP_OFF(tga);
|
|
if (tga->off != off) __TGASeek(tga, off, SEEK_SET);
|
|
if (tga->off != off) {
|
|
TGA_ERROR(tga, TGA_SEEK_FAIL);
|
|
return 0;
|
|
}
|
|
|
|
*buf = (tbyte*)malloc(n);
|
|
if (!buf) {
|
|
TGA_ERROR(tga, TGA_OOM);
|
|
return 0;
|
|
}
|
|
|
|
if ((read = TGARead(tga, *buf, n, 1)) != 1) {
|
|
TGA_ERROR(tga, TGA_READ_FAIL);
|
|
return 0;
|
|
}
|
|
|
|
if (TGA_CAN_SWAP(tga->hdr.map_entry) && (flags & TGA_RGB)) {
|
|
__TGAbgr2rgb(*buf, TGA_CMAP_SIZE(tga), tga->hdr.map_entry / 8);
|
|
}
|
|
|
|
if (tga->hdr.map_entry == 15 || tga->hdr.map_entry == 16) {
|
|
n = read + read / 2;
|
|
*buf = (tbyte*)realloc(*buf, n);
|
|
if (!(*buf)) {
|
|
TGA_ERROR(tga, TGA_OOM);
|
|
return 0;
|
|
}
|
|
|
|
for(i = read - 1; i >= 0; i -= 2) {
|
|
tmp = *buf[i - 1] + *buf[i] * 255;
|
|
*buf[n - 2] = (tmp >> 10) & 0x1F;
|
|
*buf[n - 1] = (tmp >> 5) & 0x1F;
|
|
*buf[n] = (tmp >> 5) & 0x1F;
|
|
n -= 3;
|
|
}
|
|
}
|
|
|
|
tga->last = TGA_OK;
|
|
return read;
|
|
}
|
|
|
|
int
|
|
TGAReadRLE(TGA *tga,
|
|
tbyte *buf)
|
|
{
|
|
int head;
|
|
char sample[4];
|
|
tbyte k, repeat = 0, direct = 0, bytes = tga->hdr.depth / 8;
|
|
tshort x;
|
|
tshort width = tga->hdr.width;
|
|
FILE *fd = tga->fd;
|
|
|
|
if (!tga || !buf) return TGA_ERROR;
|
|
|
|
for (x = 0; x < width; ++x) {
|
|
if (repeat == 0 && direct == 0) {
|
|
head = getc(fd);
|
|
if (head == EOF) return TGA_ERROR;
|
|
if (head >= 128) {
|
|
repeat = head - 127;
|
|
if (fread(sample, bytes, 1, fd) < 1)
|
|
return TGA_ERROR;
|
|
} else {
|
|
direct = head + 1;
|
|
}
|
|
}
|
|
if (repeat > 0) {
|
|
for (k = 0; k < bytes; ++k) buf[k] = sample[k];
|
|
--repeat;
|
|
} else {
|
|
if (fread(buf, bytes, 1, fd) < 1) return TGA_ERROR;
|
|
--direct;
|
|
}
|
|
buf += bytes;
|
|
}
|
|
|
|
tga->last = TGA_OK;
|
|
return TGA_OK;
|
|
}
|
|
|
|
size_t
|
|
TGAReadScanlines(TGA *tga,
|
|
tbyte *buf,
|
|
size_t sln,
|
|
size_t n,
|
|
tuint32 flags)
|
|
{
|
|
tlong i, off;
|
|
size_t sln_size, read, tmp;
|
|
|
|
if (!tga || !buf) return 0;
|
|
|
|
sln_size = TGA_SCANLINE_SIZE(tga);
|
|
off = TGA_IMG_DATA_OFF(tga) + (sln * sln_size);
|
|
|
|
if (tga->off != off) __TGASeek(tga, off, SEEK_SET);
|
|
if (tga->off != off) {
|
|
TGA_ERROR(tga, TGA_SEEK_FAIL);
|
|
return 0;
|
|
}
|
|
|
|
if(TGA_IS_ENCODED(tga)) {
|
|
for(read = 0; read <= n; ++read) {
|
|
if(TGAReadRLE(tga, buf + ((sln + read) * sln_size)) !=
|
|
TGA_OK) break;
|
|
}
|
|
tga->hdr.img_t -= 8;
|
|
} else {
|
|
read = TGARead(tga, buf, sln_size, n);
|
|
}
|
|
if(read != n) {
|
|
TGA_ERROR(tga, TGA_READ_FAIL);
|
|
return read;
|
|
}
|
|
|
|
if (TGA_CAN_SWAP(tga->hdr.depth) && (flags & TGA_RGB)) {
|
|
__TGAbgr2rgb(buf + (sln_size * sln), sln_size * n,
|
|
tga->hdr.depth / 8);
|
|
}
|
|
|
|
if (tga->hdr.depth == 15 || tga->hdr.depth == 16) {
|
|
n = read + read / 2;
|
|
buf = (tbyte*)realloc(buf, n);
|
|
if (!buf) {
|
|
TGA_ERROR(tga, TGA_OOM);
|
|
return 0;
|
|
}
|
|
|
|
for(i = read - 1; i >= 0; i -= 2) {
|
|
tmp = buf[i - 1] + buf[i] * 255;
|
|
buf[n - 2] = (tmp >> 10) & 0x1F;
|
|
buf[n - 1] = (tmp >> 5) & 0x1F;
|
|
buf[n] = (tmp >> 5) & 0x1F;
|
|
n -= 3;
|
|
}
|
|
}
|
|
|
|
tga->last = TGA_OK;
|
|
return read;
|
|
}
|