You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
315 lines
8.2 KiB
315 lines
8.2 KiB
/* |
|
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 "mathlib.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; |
|
byte palette[256][4], 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 byte *bufend, *in; |
|
byte *buffer, *out; |
|
const char *comment = "Generated by Xash ImageLib\0"; |
|
|
|
if( FS_FileExists( name, false ) && !Image_CheckFlag( IL_ALLOW_OVERWRITE )) |
|
return false; // already existed |
|
|
|
if( pix->flags & IMAGE_HAS_ALPHA ) |
|
outsize = pix->width * pix->height * 4 + 18 + Q_strlen( comment ); |
|
else outsize = pix->width * pix->height * 3 + 18 + Q_strlen( comment ); |
|
|
|
buffer = (byte *)Mem_Calloc( host.imagepool, outsize ); |
|
|
|
// prepare header |
|
buffer[0] = Q_strlen( comment ); // tga comment length |
|
buffer[2] = 2; // uncompressed type |
|
buffer[12] = (pix->width >> 0) & 0xFF; |
|
buffer[13] = (pix->width >> 8) & 0xFF; |
|
buffer[14] = (pix->height >> 0) & 0xFF; |
|
buffer[15] = (pix->height >> 8) & 0xFF; |
|
buffer[16] = ( pix->flags & IMAGE_HAS_ALPHA ) ? 32 : 24; |
|
buffer[17] = ( pix->flags & IMAGE_HAS_ALPHA ) ? 8 : 0; // 8 bits of alpha |
|
Q_strncpy( buffer + 18, comment, Q_strlen( comment )); |
|
out = buffer + 18 + Q_strlen( comment ); |
|
|
|
// 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: |
|
Mem_Free( buffer ); |
|
return false; |
|
} |
|
|
|
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; |
|
}
|
|
|