mirror of
https://github.com/YGGverse/xash3d-fwgs.git
synced 2025-01-08 22:27:53 +00: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:]]\+$//' {} \+ ```
450 lines
11 KiB
C
450 lines
11 KiB
C
/*
|
|
img_bmp.c - bmp 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_bmp.h"
|
|
|
|
/*
|
|
=============
|
|
Image_LoadBMP
|
|
=============
|
|
*/
|
|
qboolean Image_LoadBMP( const char *name, const byte *buffer, fs_offset_t filesize )
|
|
{
|
|
byte *buf_p, *pixbuf;
|
|
rgba_t palette[256];
|
|
int i, columns, column, rows, row, bpp = 1;
|
|
int cbPalBytes = 0, padSize = 0, bps = 0;
|
|
int reflectivity[3] = { 0, 0, 0 };
|
|
qboolean load_qfont = false;
|
|
bmp_t bhdr;
|
|
fs_offset_t estimatedSize;
|
|
|
|
if( filesize < sizeof( bhdr ))
|
|
{
|
|
Con_Reportf( S_ERROR "Image_LoadBMP: %s have incorrect file size %li should be greater than %li (header)\n", name, filesize, sizeof( bhdr ) );
|
|
return false;
|
|
}
|
|
|
|
buf_p = (byte *)buffer;
|
|
memcpy( &bhdr, buf_p, sizeof( bmp_t ));
|
|
buf_p += sizeof( bmp_t );
|
|
|
|
// bogus file header check
|
|
if( bhdr.reserved0 != 0 ) return false;
|
|
if( bhdr.planes != 1 ) return false;
|
|
|
|
if( memcmp( bhdr.id, "BM", 2 ))
|
|
{
|
|
Con_DPrintf( S_ERROR "Image_LoadBMP: only Windows-style BMP files supported (%s)\n", name );
|
|
return false;
|
|
}
|
|
|
|
if( bhdr.bitmapHeaderSize != 0x28 )
|
|
{
|
|
Con_DPrintf( S_ERROR "Image_LoadBMP: invalid header size %i\n", bhdr.bitmapHeaderSize );
|
|
return false;
|
|
}
|
|
|
|
// bogus info header check
|
|
if( bhdr.fileSize != filesize )
|
|
{
|
|
// Sweet Half-Life issues. splash.bmp have bogus filesize
|
|
Con_Reportf( S_WARN "Image_LoadBMP: %s have incorrect file size %li should be %i\n", name, filesize, bhdr.fileSize );
|
|
}
|
|
|
|
// bogus compression? Only non-compressed supported.
|
|
if( bhdr.compression != BI_RGB )
|
|
{
|
|
Con_DPrintf( S_ERROR "Image_LoadBMP: only uncompressed BMP files supported (%s)\n", name );
|
|
return false;
|
|
}
|
|
|
|
image.width = columns = bhdr.width;
|
|
image.height = rows = abs( bhdr.height );
|
|
|
|
if( !Image_ValidSize( name ))
|
|
return false;
|
|
|
|
// special case for loading qfont (menu font)
|
|
if( !Q_strncmp( name, "#XASH_SYSTEMFONT_001", 20 ))
|
|
{
|
|
// NOTE: same as system font we can use 4-bit bmps only
|
|
// step1: move main layer into alpha-channel (give grayscale from RED channel)
|
|
// step2: fill main layer with 255 255 255 color (white)
|
|
// step3: ????
|
|
// step4: PROFIT!!! (economy up to 150 kb for menu.dll final size)
|
|
image.flags |= IMAGE_HAS_ALPHA;
|
|
load_qfont = true;
|
|
}
|
|
|
|
if( bhdr.bitsPerPixel <= 8 )
|
|
{
|
|
// figure out how many entries are actually in the table
|
|
if( bhdr.colors == 0 )
|
|
{
|
|
bhdr.colors = 256;
|
|
cbPalBytes = ( 1 << bhdr.bitsPerPixel ) * sizeof( rgba_t );
|
|
}
|
|
else cbPalBytes = bhdr.colors * sizeof( rgba_t );
|
|
}
|
|
|
|
estimatedSize = ( buf_p - buffer ) + cbPalBytes;
|
|
if( filesize < estimatedSize )
|
|
{
|
|
Con_Reportf( S_ERROR "Image_LoadBMP: %s have incorrect file size %li should be greater than %li (palette)\n", name, filesize, estimatedSize );
|
|
return false;
|
|
}
|
|
|
|
memcpy( palette, buf_p, cbPalBytes );
|
|
|
|
// setup gradient alpha for player decal
|
|
if( !Q_strncmp( name, "#logo", 5 ))
|
|
{
|
|
for( i = 0; i < bhdr.colors; i++ )
|
|
palette[i][3] = i;
|
|
image.flags |= IMAGE_HAS_ALPHA;
|
|
}
|
|
|
|
if( Image_CheckFlag( IL_OVERVIEW ) && bhdr.bitsPerPixel == 8 )
|
|
{
|
|
// convert green background into alpha-layer, make opacity for all other entries
|
|
for( i = 0; i < bhdr.colors; i++ )
|
|
{
|
|
if( palette[i][0] == 0 && palette[i][1] == 255 && palette[i][2] == 0 )
|
|
{
|
|
palette[i][0] = palette[i][1] = palette[i][2] = palette[i][3] = 0;
|
|
image.flags |= IMAGE_HAS_ALPHA;
|
|
}
|
|
else palette[i][3] = 255;
|
|
}
|
|
}
|
|
|
|
if( Image_CheckFlag( IL_KEEP_8BIT ) && bhdr.bitsPerPixel == 8 )
|
|
{
|
|
pixbuf = image.palette = Mem_Malloc( host.imagepool, 1024 );
|
|
|
|
// bmp have a reversed palette colors
|
|
for( i = 0; i < bhdr.colors; i++ )
|
|
{
|
|
*pixbuf++ = palette[i][2];
|
|
*pixbuf++ = palette[i][1];
|
|
*pixbuf++ = palette[i][0];
|
|
*pixbuf++ = palette[i][3];
|
|
}
|
|
image.type = PF_INDEXED_32; // 32 bit palette
|
|
}
|
|
else
|
|
{
|
|
image.palette = NULL;
|
|
image.type = PF_RGBA_32;
|
|
bpp = 4;
|
|
}
|
|
|
|
buf_p += cbPalBytes;
|
|
bps = image.width * (bhdr.bitsPerPixel >> 3);
|
|
|
|
switch( bhdr.bitsPerPixel )
|
|
{
|
|
case 1:
|
|
padSize = (( 32 - ( bhdr.width % 32 )) / 8 ) % 4;
|
|
break;
|
|
case 4:
|
|
padSize = (( 8 - ( bhdr.width % 8 )) / 2 ) % 4;
|
|
break;
|
|
case 16:
|
|
padSize = ( 4 - ( image.width * 2 % 4 )) % 4;
|
|
break;
|
|
case 8:
|
|
case 24:
|
|
padSize = ( 4 - ( bps % 4 )) % 4;
|
|
break;
|
|
}
|
|
|
|
estimatedSize = ( buf_p - buffer ) + ( image.width + padSize ) * image.height * ( bhdr.bitsPerPixel >> 3 );
|
|
if( filesize < estimatedSize )
|
|
{
|
|
if( image.palette )
|
|
{
|
|
Mem_Free( image.palette );
|
|
image.palette = NULL;
|
|
}
|
|
|
|
Con_Reportf( S_ERROR "Image_LoadBMP: %s have incorrect file size %li should be greater than %li (pixels)\n", name, filesize, estimatedSize );
|
|
return false;
|
|
}
|
|
|
|
image.size = image.width * image.height * bpp;
|
|
image.rgba = Mem_Malloc( host.imagepool, image.size );
|
|
|
|
for( row = rows - 1; row >= 0; row-- )
|
|
{
|
|
pixbuf = image.rgba + (row * columns * bpp);
|
|
|
|
for( column = 0; column < columns; column++ )
|
|
{
|
|
byte red, green, blue, alpha;
|
|
word shortPixel;
|
|
int c, k, palIndex;
|
|
|
|
switch( bhdr.bitsPerPixel )
|
|
{
|
|
case 1:
|
|
alpha = *buf_p++;
|
|
column--; // ingnore main iterations
|
|
for( c = 0, k = 128; c < 8; c++, k >>= 1 )
|
|
{
|
|
red = green = blue = (!!(alpha & k) == 1 ? 0xFF : 0x00);
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = 0x00;
|
|
if( ++column == columns )
|
|
break;
|
|
}
|
|
break;
|
|
case 4:
|
|
alpha = *buf_p++;
|
|
palIndex = alpha >> 4;
|
|
if( load_qfont )
|
|
{
|
|
*pixbuf++ = red = 255;
|
|
*pixbuf++ = green = 255;
|
|
*pixbuf++ = blue = 255;
|
|
*pixbuf++ = palette[palIndex][2];
|
|
}
|
|
else
|
|
{
|
|
*pixbuf++ = red = palette[palIndex][2];
|
|
*pixbuf++ = green = palette[palIndex][1];
|
|
*pixbuf++ = blue = palette[palIndex][0];
|
|
*pixbuf++ = palette[palIndex][3];
|
|
}
|
|
if( ++column == columns ) break;
|
|
palIndex = alpha & 0x0F;
|
|
if( load_qfont )
|
|
{
|
|
*pixbuf++ = red = 255;
|
|
*pixbuf++ = green = 255;
|
|
*pixbuf++ = blue = 255;
|
|
*pixbuf++ = palette[palIndex][2];
|
|
}
|
|
else
|
|
{
|
|
*pixbuf++ = red = palette[palIndex][2];
|
|
*pixbuf++ = green = palette[palIndex][1];
|
|
*pixbuf++ = blue = palette[palIndex][0];
|
|
*pixbuf++ = palette[palIndex][3];
|
|
}
|
|
break;
|
|
case 8:
|
|
palIndex = *buf_p++;
|
|
red = palette[palIndex][2];
|
|
green = palette[palIndex][1];
|
|
blue = palette[palIndex][0];
|
|
alpha = palette[palIndex][3];
|
|
|
|
if( Image_CheckFlag( IL_KEEP_8BIT ))
|
|
{
|
|
*pixbuf++ = palIndex;
|
|
}
|
|
else
|
|
{
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = alpha;
|
|
}
|
|
break;
|
|
case 16:
|
|
shortPixel = *(word *)buf_p, buf_p += 2;
|
|
*pixbuf++ = blue = (shortPixel & ( 31 << 10 )) >> 7;
|
|
*pixbuf++ = green = (shortPixel & ( 31 << 5 )) >> 2;
|
|
*pixbuf++ = red = (shortPixel & ( 31 )) << 3;
|
|
*pixbuf++ = 0xff;
|
|
break;
|
|
case 24:
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = 0xFF;
|
|
break;
|
|
case 32:
|
|
blue = *buf_p++;
|
|
green = *buf_p++;
|
|
red = *buf_p++;
|
|
alpha = *buf_p++;
|
|
*pixbuf++ = red;
|
|
*pixbuf++ = green;
|
|
*pixbuf++ = blue;
|
|
*pixbuf++ = alpha;
|
|
if( alpha != 255 ) image.flags |= IMAGE_HAS_ALPHA;
|
|
break;
|
|
default:
|
|
Mem_Free( image.palette );
|
|
Mem_Free( image.rgba );
|
|
return false;
|
|
}
|
|
|
|
if( red != green || green != blue )
|
|
image.flags |= IMAGE_HAS_COLOR;
|
|
|
|
reflectivity[0] += red;
|
|
reflectivity[1] += green;
|
|
reflectivity[2] += blue;
|
|
}
|
|
buf_p += padSize; // actual only for 4-bit bmps
|
|
}
|
|
|
|
VectorDivide( reflectivity, ( image.width * image.height ), image.fogParams );
|
|
if( image.palette ) Image_GetPaletteBMP( image.palette );
|
|
image.depth = 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
qboolean Image_SaveBMP( const char *name, rgbdata_t *pix )
|
|
{
|
|
file_t *pfile = NULL;
|
|
size_t total_size, cur_size;
|
|
rgba_t rgrgbPalette[256];
|
|
dword cbBmpBits;
|
|
byte *clipbuf = NULL;
|
|
byte *pb, *pbBmpBits;
|
|
dword cbPalBytes;
|
|
dword biTrueWidth;
|
|
int pixel_size;
|
|
int i, x, y;
|
|
bmp_t hdr;
|
|
|
|
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_INDEXED_24:
|
|
case PF_INDEXED_32:
|
|
pixel_size = 1;
|
|
break;
|
|
case PF_RGB_24:
|
|
pixel_size = 3;
|
|
break;
|
|
case PF_RGBA_32:
|
|
pixel_size = 4;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
pfile = FS_Open( name, "wb", false );
|
|
if( !pfile ) return false;
|
|
|
|
// NOTE: align transparency column will sucessfully removed
|
|
// after create sprite or lump image, it's just standard requiriments
|
|
biTrueWidth = ((pix->width + 3) & ~3);
|
|
cbBmpBits = biTrueWidth * pix->height * pixel_size;
|
|
cbPalBytes = ( pixel_size == 1 ) ? 256 * sizeof( rgba_t ) : 0;
|
|
|
|
// Bogus file header check
|
|
hdr.id[0] = 'B';
|
|
hdr.id[1] = 'M';
|
|
hdr.fileSize = sizeof( hdr ) + cbBmpBits + cbPalBytes;
|
|
hdr.reserved0 = 0;
|
|
hdr.bitmapDataOffset = sizeof( hdr ) + cbPalBytes;
|
|
hdr.bitmapHeaderSize = BI_SIZE;
|
|
hdr.width = biTrueWidth;
|
|
hdr.height = pix->height;
|
|
hdr.planes = 1;
|
|
hdr.bitsPerPixel = pixel_size * 8;
|
|
hdr.compression = BI_RGB;
|
|
hdr.bitmapDataSize = cbBmpBits;
|
|
hdr.hRes = 0;
|
|
hdr.vRes = 0;
|
|
hdr.colors = ( pixel_size == 1 ) ? 256 : 0;
|
|
hdr.importantColors = 0;
|
|
|
|
FS_Write( pfile, &hdr, sizeof( bmp_t ));
|
|
|
|
pbBmpBits = Mem_Malloc( host.imagepool, cbBmpBits );
|
|
|
|
if( pixel_size == 1 )
|
|
{
|
|
pb = pix->palette;
|
|
|
|
// copy over used entries
|
|
for( i = 0; i < (int)hdr.colors; i++ )
|
|
{
|
|
rgrgbPalette[i][2] = *pb++;
|
|
rgrgbPalette[i][1] = *pb++;
|
|
rgrgbPalette[i][0] = *pb++;
|
|
|
|
// bmp feature - can store 32-bit palette if present
|
|
// some viewers e.g. fimg.exe can show alpha-chanell for it
|
|
if( pix->type == PF_INDEXED_32 )
|
|
rgrgbPalette[i][3] = *pb++;
|
|
else rgrgbPalette[i][3] = 0;
|
|
}
|
|
|
|
// write palette
|
|
FS_Write( pfile, rgrgbPalette, cbPalBytes );
|
|
}
|
|
|
|
pb = pix->buffer;
|
|
|
|
for( y = 0; y < hdr.height; y++ )
|
|
{
|
|
i = (hdr.height - 1 - y ) * (hdr.width);
|
|
|
|
for( x = 0; x < pix->width; x++ )
|
|
{
|
|
if( pixel_size == 1 )
|
|
{
|
|
// 8-bit
|
|
pbBmpBits[i] = pb[x];
|
|
}
|
|
else
|
|
{
|
|
// 24 bit
|
|
pbBmpBits[i*pixel_size+0] = pb[x*pixel_size+2];
|
|
pbBmpBits[i*pixel_size+1] = pb[x*pixel_size+1];
|
|
pbBmpBits[i*pixel_size+2] = pb[x*pixel_size+0];
|
|
}
|
|
|
|
if( pixel_size == 4 ) // write alpha channel
|
|
pbBmpBits[i*pixel_size+3] = pb[x*pixel_size+3];
|
|
i++;
|
|
}
|
|
|
|
pb += pix->width * pixel_size;
|
|
}
|
|
|
|
// write bitmap bits (remainder of file)
|
|
FS_Write( pfile, pbBmpBits, cbBmpBits );
|
|
FS_Close( pfile );
|
|
|
|
Mem_Free( pbBmpBits );
|
|
|
|
return true;
|
|
}
|