mirror of
https://github.com/FWGS/xash3d-fwgs
synced 2024-11-28 13:02:13 +01:00
5e0a0765ce
The `.editorconfig` file in this repo is configured to trim all trailing whitespace regardless of whether the line is modified. Trims all trailing whitespace in the repository to make the codebase easier to work with in editors that respect `.editorconfig`. `git blame` becomes less useful on these lines but it already isn't very useful. Commands: ``` find . -type f -name '*.h' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ find . -type f -name '*.c' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ ```
336 lines
8.3 KiB
C
336 lines
8.3 KiB
C
/*
|
|
img_tga.c - tga format load & save
|
|
Copyright (C) 2007 Uncle Mike
|
|
|
|
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 3 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.
|
|
*/
|
|
|
|
#include "imagelib.h"
|
|
#include "xash3d_mathlib.h"
|
|
#include "img_tga.h"
|
|
|
|
/*
|
|
=============
|
|
Image_LoadTGA
|
|
=============
|
|
*/
|
|
qboolean Image_LoadTGA( const char *name, const byte *buffer, fs_offset_t filesize )
|
|
{
|
|
int i, columns, rows, row_inc, row, col;
|
|
byte *buf_p, *pixbuf, *targa_rgba;
|
|
rgba_t palette[256];
|
|
byte red = 0, green = 0, blue = 0, alpha = 0;
|
|
int readpixelcount, pixelcount;
|
|
int reflectivity[3] = { 0, 0, 0 };
|
|
qboolean compressed;
|
|
tga_t targa_header;
|
|
|
|
if( filesize < sizeof( tga_t ))
|
|
return false;
|
|
|
|
buf_p = (byte *)buffer;
|
|
targa_header.id_length = *buf_p++;
|
|
targa_header.colormap_type = *buf_p++;
|
|
targa_header.image_type = *buf_p++;
|
|
|
|
targa_header.colormap_index = buf_p[0] + buf_p[1] * 256; buf_p += 2;
|
|
targa_header.colormap_length = buf_p[0] + buf_p[1] * 256; buf_p += 2;
|
|
targa_header.colormap_size = *buf_p; buf_p += 1;
|
|
targa_header.x_origin = *(short *)buf_p; buf_p += 2;
|
|
targa_header.y_origin = *(short *)buf_p; buf_p += 2;
|
|
targa_header.width = image.width = *(short *)buf_p; buf_p += 2;
|
|
targa_header.height = image.height = *(short *)buf_p; buf_p += 2;
|
|
targa_header.pixel_size = *buf_p++;
|
|
targa_header.attributes = *buf_p++;
|
|
if( targa_header.id_length != 0 ) buf_p += targa_header.id_length; // skip TARGA image comment
|
|
|
|
// check for tga file
|
|
if( !Image_ValidSize( name )) return false;
|
|
|
|
image.type = PF_RGBA_32; // always exctracted to 32-bit buffer
|
|
|
|
if( targa_header.image_type == 1 || targa_header.image_type == 9 )
|
|
{
|
|
// uncompressed colormapped image
|
|
if( targa_header.pixel_size != 8 )
|
|
{
|
|
Con_DPrintf( S_ERROR "Image_LoadTGA: (%s) Only 8 bit images supported for type 1 and 9\n", name );
|
|
return false;
|
|
}
|
|
if( targa_header.colormap_length != 256 )
|
|
{
|
|
Con_DPrintf( S_ERROR "Image_LoadTGA: (%s) Only 8 bit colormaps are supported for type 1 and 9\n", name );
|
|
return false;
|
|
}
|
|
if( targa_header.colormap_index )
|
|
{
|
|
Con_DPrintf( S_ERROR "Image_LoadTGA: (%s) colormap_index is not supported for type 1 and 9\n", name );
|
|
return false;
|
|
}
|
|
if( targa_header.colormap_size == 24 )
|
|
{
|
|
for( i = 0; i < targa_header.colormap_length; i++ )
|
|
{
|
|
palette[i][2] = *buf_p++;
|
|
palette[i][1] = *buf_p++;
|
|
palette[i][0] = *buf_p++;
|
|
palette[i][3] = 255;
|
|
}
|
|
}
|
|
else if( targa_header.colormap_size == 32 )
|
|
{
|
|
for( i = 0; i < targa_header.colormap_length; i++ )
|
|
{
|
|
palette[i][2] = *buf_p++;
|
|
palette[i][1] = *buf_p++;
|
|
palette[i][0] = *buf_p++;
|
|
palette[i][3] = *buf_p++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Con_DPrintf( S_ERROR "Image_LoadTGA: (%s) only 24 and 32 bit colormaps are supported for type 1 and 9\n", name );
|
|
return false;
|
|
}
|
|
}
|
|
else if( targa_header.image_type == 2 || targa_header.image_type == 10 )
|
|
{
|
|
// uncompressed or RLE compressed RGB
|
|
if( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 )
|
|
{
|
|
Con_DPrintf( S_ERROR "Image_LoadTGA: (%s) Only 32 or 24 bit images supported for type 2 and 10\n", name );
|
|
return false;
|
|
}
|
|
}
|
|
else if( targa_header.image_type == 3 || targa_header.image_type == 11 )
|
|
{
|
|
// uncompressed greyscale
|
|
if( targa_header.pixel_size != 8 && targa_header.pixel_size != 16 )
|
|
{
|
|
Con_DPrintf( S_ERROR "Image_LoadTGA: (%s) Only 8 bit images supported for type 3 and 11\n", name );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
columns = targa_header.width;
|
|
rows = targa_header.height;
|
|
|
|
image.size = image.width * image.height * 4;
|
|
targa_rgba = image.rgba = Mem_Malloc( host.imagepool, image.size );
|
|
|
|
// if bit 5 of attributes isn't set, the image has been stored from bottom to top
|
|
if( !Image_CheckFlag( IL_DONTFLIP_TGA ) && targa_header.attributes & 0x20 )
|
|
{
|
|
pixbuf = targa_rgba;
|
|
row_inc = 0;
|
|
}
|
|
else
|
|
{
|
|
pixbuf = targa_rgba + ( rows - 1 ) * columns * 4;
|
|
row_inc = -columns * 4 * 2;
|
|
}
|
|
|
|
compressed = ( targa_header.image_type == 9 || targa_header.image_type == 10 || targa_header.image_type == 11 );
|
|
for( row = col = 0; row < rows; )
|
|
{
|
|
pixelcount = 0x10000;
|
|
readpixelcount = 0x10000;
|
|
|
|
if( compressed )
|
|
{
|
|
pixelcount = *buf_p++;
|
|
if( pixelcount & 0x80 ) // run-length packet
|
|
readpixelcount = 1;
|
|
pixelcount = 1 + ( pixelcount & 0x7f );
|
|
}
|
|
|
|
while( pixelcount-- && ( row < rows ) )
|
|
{
|
|
if( readpixelcount-- > 0 )
|
|
{
|
|
switch( targa_header.image_type )
|
|
{
|
|
case 1:
|
|
case 9:
|
|
// colormapped image
|
|
blue = *buf_p++;
|
|
if( blue < targa_header.colormap_length )
|
|
{
|
|
red = palette[blue][0];
|
|
green = palette[blue][1];
|
|
alpha = palette[blue][3];
|
|
blue = palette[blue][2];
|
|
if( alpha != 255 ) image.flags |= IMAGE_HAS_ALPHA;
|
|
}
|
|
break;
|
|
case 2:
|
|
case 10:
|
|
// 24 or 32 bit image
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
alpha = 255;
|
|
if( targa_header.pixel_size == 32 )
|
|
{
|
|
alpha = *buf_p++;
|
|
if( alpha != 255 )
|
|
image.flags |= IMAGE_HAS_ALPHA;
|
|
}
|
|
break;
|
|
case 3:
|
|
case 11:
|
|
// greyscale image
|
|
blue = green = red = *buf_p++;
|
|
if( targa_header.pixel_size == 16 )
|
|
{
|
|
alpha = *buf_p++;
|
|
if( alpha != 255 )
|
|
image.flags |= IMAGE_HAS_ALPHA;
|
|
}
|
|
else
|
|
alpha = 255;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( red != green || green != blue )
|
|
image.flags |= IMAGE_HAS_COLOR;
|
|
|
|
reflectivity[0] += red;
|
|
reflectivity[1] += green;
|
|
reflectivity[2] += blue;
|
|
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = alpha;
|
|
if( ++col == columns )
|
|
{
|
|
// run spans across rows
|
|
row++;
|
|
col = 0;
|
|
pixbuf += row_inc;
|
|
}
|
|
}
|
|
}
|
|
|
|
VectorDivide( reflectivity, ( image.width * image.height ), image.fogParams );
|
|
image.depth = 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
Image_SaveTGA
|
|
=============
|
|
*/
|
|
qboolean Image_SaveTGA( const char *name, rgbdata_t *pix )
|
|
{
|
|
int y, outsize, pixel_size;
|
|
const uint8_t *bufend, *in;
|
|
uint8_t *buffer, *out;
|
|
tga_t targa_header = {0};
|
|
const char comment[] = "Generated by Xash ImageLib";
|
|
|
|
if( FS_FileExists( name, false ) && !Image_CheckFlag( IL_ALLOW_OVERWRITE ))
|
|
return false; // already existed
|
|
|
|
// bogus parameter check
|
|
if( !pix->buffer )
|
|
return false;
|
|
|
|
// get image description
|
|
switch( pix->type )
|
|
{
|
|
case PF_RGB_24:
|
|
case PF_BGR_24: pixel_size = 3; break;
|
|
case PF_RGBA_32:
|
|
case PF_BGRA_32: pixel_size = 4; break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
outsize = pix->width * pix->height * pixel_size;
|
|
outsize += sizeof( tga_t );
|
|
outsize += sizeof( comment ) - 1;
|
|
|
|
buffer = (uint8_t *)Mem_Malloc( host.imagepool, outsize );
|
|
|
|
// prepare header
|
|
targa_header.id_length = sizeof( comment ) - 1; // tga comment length
|
|
targa_header.image_type = 2; // uncompressed type
|
|
targa_header.width = pix->width;
|
|
targa_header.height = pix->height;
|
|
|
|
if( pix->flags & IMAGE_HAS_ALPHA )
|
|
{
|
|
targa_header.pixel_size = 32;
|
|
targa_header.attributes = 8; // 8 bits of alpha
|
|
}
|
|
else
|
|
{
|
|
targa_header.pixel_size = 24;
|
|
targa_header.attributes = 0;
|
|
}
|
|
|
|
out = buffer;
|
|
|
|
memcpy( out, &targa_header, sizeof( tga_t ) );
|
|
out += sizeof( tga_t );
|
|
|
|
memcpy( out, comment, sizeof( comment ) - 1 );
|
|
out += sizeof( comment ) - 1;
|
|
|
|
switch( pix->type )
|
|
{
|
|
case PF_RGB_24:
|
|
case PF_RGBA_32:
|
|
// swap rgba to bgra and flip upside down
|
|
for( y = pix->height - 1; y >= 0; y-- )
|
|
{
|
|
in = pix->buffer + y * pix->width * pixel_size;
|
|
bufend = in + pix->width * pixel_size;
|
|
for( ; in < bufend; in += pixel_size )
|
|
{
|
|
*out++ = in[2];
|
|
*out++ = in[1];
|
|
*out++ = in[0];
|
|
if( pix->flags & IMAGE_HAS_ALPHA )
|
|
*out++ = in[3];
|
|
}
|
|
}
|
|
break;
|
|
case PF_BGR_24:
|
|
case PF_BGRA_32:
|
|
// flip upside down
|
|
for( y = pix->height - 1; y >= 0; y-- )
|
|
{
|
|
in = pix->buffer + y * pix->width * pixel_size;
|
|
bufend = in + pix->width * pixel_size;
|
|
for( ; in < bufend; in += pixel_size )
|
|
{
|
|
*out++ = in[0];
|
|
*out++ = in[1];
|
|
*out++ = in[2];
|
|
if( pix->flags & IMAGE_HAS_ALPHA )
|
|
*out++ = in[3];
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
FS_WriteFile( name, buffer, outsize );
|
|
|
|
Mem_Free( buffer );
|
|
return true;
|
|
}
|