mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-12-18 00:21:31 +00:00
59c4290563
we work even if the first pixel isn't in lower left. ;) Handling the RLE-compressed oddball cases would require a lot more code duplication, will brute force that later if nobody beats me to it with a clean solution.
358 lines
8.5 KiB
C
358 lines
8.5 KiB
C
/*
|
|
tga.c
|
|
|
|
targa image handling
|
|
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
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 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:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
static __attribute__ ((unused)) const char rcsid[] =
|
|
"$Id$";
|
|
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "QF/qendian.h"
|
|
#include "QF/quakefs.h"
|
|
#include "QF/sys.h"
|
|
#include "QF/texture.h"
|
|
#include "QF/tga.h"
|
|
#include "QF/zone.h"
|
|
|
|
#include "compat.h"
|
|
|
|
|
|
static inline byte *
|
|
blit_rgb (byte *buf, int count, byte red, byte green, byte blue)
|
|
{
|
|
while (count--) {
|
|
*buf++ = red;
|
|
*buf++ = green;
|
|
*buf++ = blue;
|
|
*buf++ = 255;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static inline byte *
|
|
blit_rgba (byte *buf, int count, byte red, byte green, byte blue, byte alpha)
|
|
{
|
|
while (count--) {
|
|
*buf++ = red;
|
|
*buf++ = green;
|
|
*buf++ = blue;
|
|
*buf++ = alpha;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static inline byte *
|
|
reverse_blit_rgb (byte *buf, int count, byte red, byte green, byte blue)
|
|
{
|
|
while (count--) {
|
|
*buf-- = 255;
|
|
*buf-- = blue;
|
|
*buf-- = green;
|
|
*buf-- = red;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static inline byte *
|
|
reverse_blit_rgba (byte *buf, int count, byte red, byte green, byte blue,
|
|
byte alpha)
|
|
{
|
|
while (count--) {
|
|
*buf-- = alpha;
|
|
*buf-- = blue;
|
|
*buf-- = green;
|
|
*buf-- = red;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static inline byte *
|
|
read_bgr (byte *buf, int count, byte **data)
|
|
{
|
|
byte blue = *(*data)++;
|
|
byte green = *(*data)++;
|
|
byte red = *(*data)++;
|
|
|
|
return blit_rgb (buf, count, red, green, blue);
|
|
}
|
|
|
|
static inline byte *
|
|
read_bgra (byte *buf, int count, byte **data)
|
|
{
|
|
byte blue = *(*data)++;
|
|
byte green = *(*data)++;
|
|
byte red = *(*data)++;
|
|
byte alpha = *(*data)++;
|
|
|
|
return blit_rgba (buf, count, red, green, blue, alpha);
|
|
}
|
|
|
|
static inline byte *
|
|
read_rgb (byte *buf, int count, byte **data)
|
|
{
|
|
byte red = *(*data)++;
|
|
byte green = *(*data)++;
|
|
byte blue = *(*data)++;
|
|
|
|
return blit_rgb (buf, count, red, green, blue);
|
|
}
|
|
|
|
static inline byte *
|
|
read_rgba (byte *buf, int count, byte **data)
|
|
{
|
|
byte red = *(*data)++;
|
|
byte green = *(*data)++;
|
|
byte blue = *(*data)++;
|
|
byte alpha = *(*data)++;
|
|
|
|
return blit_rgba (buf, count, red, green, blue, alpha);
|
|
}
|
|
|
|
static inline byte *
|
|
reverse_read_bgr (byte *buf, int count, byte **data)
|
|
{
|
|
byte blue = *(*data)++;
|
|
byte green = *(*data)++;
|
|
byte red = *(*data)++;
|
|
|
|
return reverse_blit_rgb (buf, count, red, green, blue);
|
|
}
|
|
|
|
static inline byte *
|
|
reverse_read_bgra (byte *buf, int count, byte **data)
|
|
{
|
|
byte blue = *(*data)++;
|
|
byte green = *(*data)++;
|
|
byte red = *(*data)++;
|
|
byte alpha = *(*data)++;
|
|
|
|
return reverse_blit_rgba (buf, count, red, green, blue, alpha);
|
|
}
|
|
|
|
struct tex_s *
|
|
LoadTGA (QFile *fin)
|
|
{
|
|
byte *dataByte, *pixcol, *pixrow;
|
|
int column, row, columns, rows, numPixels, span, targa_mark;
|
|
TargaHeader *targa;
|
|
tex_t *tex;
|
|
|
|
targa_mark = Hunk_LowMark ();
|
|
targa = Hunk_AllocName (qfs_filesize, "TGA");
|
|
Qread (fin, targa, qfs_filesize);
|
|
|
|
targa->colormap_index = LittleShort (targa->colormap_index);
|
|
targa->colormap_length = LittleShort (targa->colormap_length);
|
|
targa->x_origin = LittleShort (targa->x_origin);
|
|
targa->y_origin = LittleShort (targa->y_origin);
|
|
targa->width = LittleShort (targa->width);
|
|
targa->height = LittleShort (targa->height);
|
|
|
|
if (targa->image_type != 2 && targa->image_type != 10)
|
|
Sys_Error ("LoadTGA: Only type 2 and 10 targa RGB images supported");
|
|
|
|
if (targa->colormap_type != 0
|
|
|| (targa->pixel_size != 32 && targa->pixel_size != 24))
|
|
Sys_Error ("Texture_LoadTGA: Only 32 or 24 bit images supported "
|
|
"(no colormaps)");
|
|
|
|
columns = targa->width;
|
|
rows = targa->height;
|
|
numPixels = columns * rows;
|
|
|
|
switch (targa->pixel_size) {
|
|
case 24:
|
|
tex = Hunk_TempAlloc (field_offset (tex_t, data[numPixels * 4]));
|
|
tex->format = tex_rgb;
|
|
break;
|
|
default:
|
|
case 32:
|
|
tex = Hunk_TempAlloc (field_offset (tex_t, data[numPixels * 4]));
|
|
tex->format = tex_rgba;
|
|
break;
|
|
}
|
|
|
|
tex->width = columns;
|
|
tex->height = rows;
|
|
tex->palette = 0;
|
|
|
|
// skip TARGA image comment
|
|
dataByte = (byte *) (targa + 1);
|
|
dataByte += targa->id_length;
|
|
|
|
span = columns * 4; // tex->format
|
|
|
|
if (targa->image_type == 2) { // Uncompressed image
|
|
switch (targa->attributes & 48) {
|
|
case 0: // Origin at bottom left
|
|
pixrow = tex->data + (rows - 1) * span;
|
|
switch (targa->pixel_size) {
|
|
case 24:
|
|
for (row = rows - 1; row >= 0; row--, pixrow -= span) {
|
|
pixcol = pixrow;
|
|
for (column = 0; column < columns; column++)
|
|
pixcol = read_bgr (pixcol, 1, &dataByte);
|
|
}
|
|
break;
|
|
case 32:
|
|
for (row = rows - 1; row >= 0; row--, pixrow -= span) {
|
|
pixcol = pixrow;
|
|
for (column = 0; column < columns; column++)
|
|
pixcol = read_bgra (pixcol, 1, &dataByte);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 16: // Origin at bottom right
|
|
pixrow = tex->data + rows * span - 4;
|
|
switch (targa->pixel_size) {
|
|
case 24:
|
|
for (row = rows - 1; row >= 0; row--, pixrow -= span) {
|
|
pixcol = pixrow;
|
|
for (column = columns - 1; column >= 0; column--)
|
|
pixcol = reverse_read_bgr (pixcol, 1, &dataByte);
|
|
}
|
|
break;
|
|
case 32:
|
|
for (row = rows - 1; row >= 0; row--, pixrow -= span) {
|
|
pixcol = pixrow;
|
|
for (column = columns - 1; column >= 0; column--)
|
|
pixcol = reverse_read_bgra (pixcol, 1, &dataByte);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 32: // Origin at top left
|
|
pixrow = tex->data;
|
|
switch (targa->pixel_size) {
|
|
case 24:
|
|
for (row = 0; row < rows; row++, pixrow += span) {
|
|
pixcol = pixrow;
|
|
for (column = 0; column < columns; column++)
|
|
pixcol = read_bgr (pixcol, 1, &dataByte);
|
|
}
|
|
break;
|
|
case 32:
|
|
for (row = 0; row < rows; row++, pixrow += span) {
|
|
pixcol = pixrow;
|
|
for (column = 0; column < columns; column++)
|
|
pixcol = read_bgra (pixcol, 1, &dataByte);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 48: // Origin at top right
|
|
pixrow = tex->data + span - 4;
|
|
switch (targa->pixel_size) {
|
|
case 24:
|
|
for (row = 0; row < rows; row++, pixrow += span) {
|
|
pixcol = pixrow;
|
|
for (column = columns - 1; column >= 0; column--)
|
|
pixcol = reverse_read_bgr (pixcol, 1, &dataByte);
|
|
}
|
|
break;
|
|
case 32:
|
|
for (row = 0; row < rows; row++, pixrow += span) {
|
|
pixcol = pixrow;
|
|
for (column = columns - 1; column >= 0; column--)
|
|
pixcol = reverse_read_bgra (pixcol, 1, &dataByte);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
} else if (targa->image_type == 10) { // RLE compressed image
|
|
unsigned char packetHeader, packetSize;
|
|
|
|
byte *(*expand) (byte *buf, int count, byte **data);
|
|
|
|
pixrow = tex->data + (rows - 1) * span;
|
|
|
|
if (targa->pixel_size == 24)
|
|
expand = read_bgr;
|
|
else
|
|
expand = read_bgra;
|
|
|
|
for (row = rows - 1; row >= 0; row--, pixrow -= span) {
|
|
pixcol = pixrow;
|
|
for (column = 0; column < columns;) {
|
|
packetHeader = *dataByte++;
|
|
packetSize = 1 + (packetHeader & 0x7f);
|
|
while (packetSize > columns - column) {
|
|
int count = columns - column;
|
|
|
|
packetSize -= count;
|
|
if (packetHeader & 0x80) { // run-length packet
|
|
expand (pixcol, count, &dataByte);
|
|
} else { // non run-length packet
|
|
while (count--)
|
|
expand (pixcol, 1, &dataByte);
|
|
}
|
|
column = 0;
|
|
pixcol = (pixrow -= span);
|
|
if (--row < 0)
|
|
goto done;
|
|
}
|
|
column += packetSize;
|
|
if (packetHeader & 0x80) { // run-length packet
|
|
pixcol = expand (pixcol, packetSize, &dataByte);
|
|
} else { // non run-length packet
|
|
while (packetSize--)
|
|
pixcol = expand (pixcol, 1, &dataByte);
|
|
}
|
|
}
|
|
}
|
|
done:;
|
|
}
|
|
|
|
Hunk_FreeToLowMark (targa_mark);
|
|
return tex;
|
|
}
|
|
|
|
void
|
|
WriteTGAfile (const char *tganame, byte *data, int width, int height)
|
|
{
|
|
TargaHeader header;
|
|
|
|
memset (&header, 0, sizeof (header));
|
|
header.image_type = 2; // uncompressed type
|
|
header.width = LittleShort (width);
|
|
header.height = LittleShort (height);
|
|
header.pixel_size = 24;
|
|
|
|
QFS_WriteBuffers (tganame, 2, &header, sizeof (header), data,
|
|
width * height * 3);
|
|
}
|