mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 03:24:56 +08:00
176 lines
4.4 KiB
C
176 lines
4.4 KiB
C
/*
|
|
cl_spray.c - spray conversion for GoldSrc protocol
|
|
Copyright (C) 2025 Xash3D FWGS contributors
|
|
|
|
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 "common.h"
|
|
#include "client.h"
|
|
#include "imagelib.h"
|
|
|
|
#define SPRAY_MAX_SURFACE 12228
|
|
#define SPRAY_PALETTE_SIZE 256
|
|
#define SPRAY_PALETTE_BYTES ( SPRAY_PALETTE_SIZE * 3 )
|
|
#define SPRAY_ALPHA_THRESHOLD ( SPRAY_PALETTE_SIZE / 2 )
|
|
#define SPRAY_FILENAME "tempdecal.wad"
|
|
|
|
// adjusts spray dimensions
|
|
static void CL_AdjustSprayDimensions( int *width, int *height )
|
|
{
|
|
float aspect = (float)( *width ) / (float)( *height );
|
|
|
|
for( int h = (( *height ) / 16 ) * 16; h >= 16; h -= 16 )
|
|
{
|
|
int w = ((int)( h * aspect ) / 16 ) * 16;
|
|
if( w < 16 || w > *width )
|
|
continue;
|
|
|
|
if( w * h < SPRAY_MAX_SURFACE )
|
|
{
|
|
*width = w;
|
|
*height = h;
|
|
return;
|
|
}
|
|
}
|
|
// fallback: minimal size
|
|
*width = 16;
|
|
*height = 16;
|
|
}
|
|
|
|
// loads and prepares the image
|
|
static rgbdata_t *CL_LoadAndPrepareImage( const char *filename, int *width, int *height )
|
|
{
|
|
rgbdata_t *image = NULL;
|
|
|
|
Image_SetForceFlags( IL_KEEP_8BIT );
|
|
image = FS_LoadImage( filename, NULL, 0 );
|
|
|
|
if( !image )
|
|
return NULL;
|
|
|
|
*width = image->width;
|
|
*height = image->height;
|
|
CL_AdjustSprayDimensions( width, height );
|
|
|
|
if( *width != image->width || *height != image->height )
|
|
{
|
|
const int bpp = PFDesc[image->type].bpp;
|
|
const int palette_size = 256 * ( image->type == PF_INDEXED_32 ? 4 : 3 );
|
|
rgbdata_t *scaled;
|
|
qboolean resampled;
|
|
|
|
// resample image to fit spray size constraints
|
|
byte *resampled_buf = Image_ResampleInternal(
|
|
image->buffer, image->width, image->height,
|
|
*width, *height, image->type, &resampled
|
|
);
|
|
|
|
if( !resampled_buf )
|
|
{
|
|
FS_FreeImage( image );
|
|
return NULL;
|
|
}
|
|
|
|
scaled = Mem_Malloc( host.imagepool, sizeof( *scaled ));
|
|
*scaled = *image;
|
|
scaled->width = *width;
|
|
scaled->height = *height;
|
|
scaled->size = *width * *height * bpp;
|
|
scaled->buffer = Mem_Malloc( host.imagepool, scaled->size );
|
|
memcpy( scaled->buffer, resampled_buf, scaled->size );
|
|
|
|
if( image->palette )
|
|
{
|
|
// copy 8-bit palette for resampled bmp
|
|
scaled->palette = Mem_Malloc( host.imagepool, palette_size );
|
|
memcpy( scaled->palette, image->palette, palette_size );
|
|
}
|
|
|
|
FS_FreeImage( image );
|
|
image = scaled;
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
// converts an image to WAD3 spray or miptex format
|
|
qboolean CL_ConvertImageToWAD3( const char *filename )
|
|
{
|
|
qboolean is_indexed_img;
|
|
int width = 0, height = 0;
|
|
byte palette[SPRAY_PALETTE_BYTES];
|
|
byte *indexed = NULL;
|
|
rgbdata_t *image = NULL;
|
|
rgbdata_t *quant = NULL;
|
|
rgbdata_t temp_image = {0};
|
|
|
|
image = CL_LoadAndPrepareImage( filename, &width, &height );
|
|
if( !image )
|
|
return false;
|
|
|
|
is_indexed_img = image->palette != NULL;
|
|
if( is_indexed_img )
|
|
{
|
|
// copy bmp palette from rgba to rgb
|
|
for( int i = 0; i < 256; ++i )
|
|
{
|
|
palette[i * 3 + 0] = image->palette[i * 4 + 0]; // R
|
|
palette[i * 3 + 1] = image->palette[i * 4 + 1]; // G
|
|
palette[i * 3 + 2] = image->palette[i * 4 + 2]; // B
|
|
}
|
|
indexed = image->buffer;
|
|
}
|
|
else
|
|
{
|
|
quant = FS_CopyImage( image );
|
|
Image_Quantize( quant ); // it's so weird, it writes result to same structure as used for input data
|
|
|
|
if( !quant || !quant->buffer || !quant->palette )
|
|
goto cleanup;
|
|
|
|
// set index 255 for transparent pixels in rgba images
|
|
if( image->type == PF_RGBA_32 )
|
|
{
|
|
for( int i = 0; i < width * height; ++i )
|
|
{
|
|
if( image->buffer[i * 4 + 3] <= SPRAY_ALPHA_THRESHOLD )
|
|
quant->buffer[i] = 255;
|
|
}
|
|
}
|
|
|
|
quant->palette[255 * 3 + 0] = 0;
|
|
quant->palette[255 * 3 + 1] = 0;
|
|
quant->palette[255 * 3 + 2] = 255;
|
|
memcpy( palette, quant->palette, SPRAY_PALETTE_BYTES );
|
|
indexed = quant->buffer;
|
|
}
|
|
|
|
temp_image.width = width;
|
|
temp_image.height = height;
|
|
temp_image.type = PF_INDEXED_32;
|
|
temp_image.buffer = indexed;
|
|
temp_image.size = width * height;
|
|
temp_image.palette = palette;
|
|
|
|
if( is_indexed_img )
|
|
temp_image.flags |= IMAGE_GRADIENT_DECAL;
|
|
|
|
return FS_SaveImage( SPRAY_FILENAME, &temp_image );
|
|
|
|
cleanup:
|
|
if( image )
|
|
FS_FreeImage( image );
|
|
if( quant )
|
|
FS_FreeImage( quant );
|
|
return false;
|
|
}
|