mirror of
https://github.com/YGGverse/xash3d-fwgs.git
synced 2025-01-09 06:37: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:]]\+$//' {} \+ ```
1547 lines
39 KiB
C
1547 lines
39 KiB
C
/*
|
|
img_utils.c - image common tools
|
|
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 "mod_local.h"
|
|
|
|
#define LERPBYTE( i ) r = resamplerow1[i]; out[i] = (byte)(((( resamplerow2[i] - r ) * lerp)>>16 ) + r )
|
|
#define FILTER_SIZE 5
|
|
|
|
uint d_8toQ1table[256];
|
|
uint d_8toHLtable[256];
|
|
uint d_8to24table[256];
|
|
|
|
qboolean q1palette_init = false;
|
|
qboolean hlpalette_init = false;
|
|
|
|
static byte palette_q1[768] =
|
|
{
|
|
0,0,0,15,15,15,31,31,31,47,47,47,63,63,63,75,75,75,91,91,91,107,107,107,123,123,123,139,139,139,155,155,155,171,
|
|
171,171,187,187,187,203,203,203,219,219,219,235,235,235,15,11,7,23,15,11,31,23,11,39,27,15,47,35,19,55,43,23,63,
|
|
47,23,75,55,27,83,59,27,91,67,31,99,75,31,107,83,31,115,87,31,123,95,35,131,103,35,143,111,35,11,11,15,19,19,27,
|
|
27,27,39,39,39,51,47,47,63,55,55,75,63,63,87,71,71,103,79,79,115,91,91,127,99,99,139,107,107,151,115,115,163,123,
|
|
123,175,131,131,187,139,139,203,0,0,0,7,7,0,11,11,0,19,19,0,27,27,0,35,35,0,43,43,7,47,47,7,55,55,7,63,63,7,71,71,
|
|
7,75,75,11,83,83,11,91,91,11,99,99,11,107,107,15,7,0,0,15,0,0,23,0,0,31,0,0,39,0,0,47,0,0,55,0,0,63,0,0,71,0,0,79,
|
|
0,0,87,0,0,95,0,0,103,0,0,111,0,0,119,0,0,127,0,0,19,19,0,27,27,0,35,35,0,47,43,0,55,47,0,67,55,0,75,59,7,87,67,7,
|
|
95,71,7,107,75,11,119,83,15,131,87,19,139,91,19,151,95,27,163,99,31,175,103,35,35,19,7,47,23,11,59,31,15,75,35,19,
|
|
87,43,23,99,47,31,115,55,35,127,59,43,143,67,51,159,79,51,175,99,47,191,119,47,207,143,43,223,171,39,239,203,31,255,
|
|
243,27,11,7,0,27,19,0,43,35,15,55,43,19,71,51,27,83,55,35,99,63,43,111,71,51,127,83,63,139,95,71,155,107,83,167,123,
|
|
95,183,135,107,195,147,123,211,163,139,227,179,151,171,139,163,159,127,151,147,115,135,139,103,123,127,91,111,119,
|
|
83,99,107,75,87,95,63,75,87,55,67,75,47,55,67,39,47,55,31,35,43,23,27,35,19,19,23,11,11,15,7,7,187,115,159,175,107,
|
|
143,163,95,131,151,87,119,139,79,107,127,75,95,115,67,83,107,59,75,95,51,63,83,43,55,71,35,43,59,31,35,47,23,27,35,
|
|
19,19,23,11,11,15,7,7,219,195,187,203,179,167,191,163,155,175,151,139,163,135,123,151,123,111,135,111,95,123,99,83,
|
|
107,87,71,95,75,59,83,63,51,67,51,39,55,43,31,39,31,23,27,19,15,15,11,7,111,131,123,103,123,111,95,115,103,87,107,
|
|
95,79,99,87,71,91,79,63,83,71,55,75,63,47,67,55,43,59,47,35,51,39,31,43,31,23,35,23,15,27,19,11,19,11,7,11,7,255,
|
|
243,27,239,223,23,219,203,19,203,183,15,187,167,15,171,151,11,155,131,7,139,115,7,123,99,7,107,83,0,91,71,0,75,55,
|
|
0,59,43,0,43,31,0,27,15,0,11,7,0,0,0,255,11,11,239,19,19,223,27,27,207,35,35,191,43,43,175,47,47,159,47,47,143,47,
|
|
47,127,47,47,111,47,47,95,43,43,79,35,35,63,27,27,47,19,19,31,11,11,15,43,0,0,59,0,0,75,7,0,95,7,0,111,15,0,127,23,
|
|
7,147,31,7,163,39,11,183,51,15,195,75,27,207,99,43,219,127,59,227,151,79,231,171,95,239,191,119,247,211,139,167,123,
|
|
59,183,155,55,199,195,55,231,227,87,127,191,255,171,231,255,215,255,255,103,0,0,139,0,0,179,0,0,215,0,0,255,0,0,255,
|
|
243,147,255,247,199,255,255,255,159,91,83
|
|
};
|
|
|
|
// this is used only for particle colors
|
|
static byte palette_hl[768] =
|
|
{
|
|
0,0,0,15,15,15,31,31,31,47,47,47,63,63,63,75,75,75,91,91,91,107,107,107,123,123,123,139,139,139,155,155,155,171,
|
|
171,171,187,187,187,203,203,203,219,219,219,235,235,235,15,11,7,23,15,11,31,23,11,39,27,15,47,35,19,55,43,23,63,
|
|
47,23,75,55,27,83,59,27,91,67,31,99,75,31,107,83,31,115,87,31,123,95,35,131,103,35,143,111,35,11,11,15,19,19,27,
|
|
27,27,39,39,39,51,47,47,63,55,55,75,63,63,87,71,71,103,79,79,115,91,91,127,99,99,139,107,107,151,115,115,163,123,
|
|
123,175,131,131,187,139,139,203,0,0,0,7,7,0,11,11,0,19,19,0,27,27,0,35,35,0,43,43,7,47,47,7,55,55,7,63,63,7,71,71,
|
|
7,75,75,11,83,83,11,91,91,11,99,99,11,107,107,15,7,0,0,15,0,0,23,0,0,31,0,0,39,0,0,47,0,0,55,0,0,63,0,0,71,0,0,79,
|
|
0,0,87,0,0,95,0,0,103,0,0,111,0,0,119,0,0,127,0,0,19,19,0,27,27,0,35,35,0,47,43,0,55,47,0,67,55,0,75,59,7,87,67,7,
|
|
95,71,7,107,75,11,119,83,15,131,87,19,139,91,19,151,95,27,163,99,31,175,103,35,35,19,7,47,23,11,59,31,15,75,35,19,
|
|
87,43,23,99,47,31,115,55,35,127,59,43,143,67,51,159,79,51,175,99,47,191,119,47,207,143,43,223,171,39,239,203,31,255,
|
|
243,27,11,7,0,27,19,0,43,35,15,55,43,19,71,51,27,83,55,35,99,63,43,111,71,51,127,83,63,139,95,71,155,107,83,167,123,
|
|
95,183,135,107,195,147,123,211,163,139,227,179,151,171,139,163,159,127,151,147,115,135,139,103,123,127,91,111,119,
|
|
83,99,107,75,87,95,63,75,87,55,67,75,47,55,67,39,47,55,31,35,43,23,27,35,19,19,23,11,11,15,7,7,187,115,159,175,107,
|
|
143,163,95,131,151,87,119,139,79,107,127,75,95,115,67,83,107,59,75,95,51,63,83,43,55,71,35,43,59,31,35,47,23,27,35,
|
|
19,19,23,11,11,15,7,7,219,195,187,203,179,167,191,163,155,175,151,139,163,135,123,151,123,111,135,111,95,123,99,83,
|
|
107,87,71,95,75,59,83,63,51,67,51,39,55,43,31,39,31,23,27,19,15,15,11,7,111,131,123,103,123,111,95,115,103,87,107,
|
|
95,79,99,87,71,91,79,63,83,71,55,75,63,47,67,55,43,59,47,35,51,39,31,43,31,23,35,23,15,27,19,11,19,11,7,11,7,255,
|
|
243,27,239,223,23,219,203,19,203,183,15,187,167,15,171,151,11,155,131,7,139,115,7,123,99,7,107,83,0,91,71,0,75,55,
|
|
0,59,43,0,43,31,0,27,15,0,11,7,0,0,0,255,11,11,239,19,19,223,27,27,207,35,35,191,43,43,175,47,47,159,47,47,143,47,
|
|
47,127,47,47,111,47,47,95,43,43,79,35,35,63,27,27,47,19,19,31,11,11,15,43,0,0,59,0,0,75,7,0,95,7,0,111,15,0,127,23,
|
|
7,147,31,7,163,39,11,183,51,15,195,75,27,207,99,43,219,127,59,227,151,79,231,171,95,239,191,119,247,211,139,167,123,
|
|
59,183,155,55,199,195,55,231,227,87,0,255,0,171,231,255,215,255,255,103,0,0,139,0,0,179,0,0,215,0,0,255,0,0,255,243,
|
|
147,255,247,199,255,255,255,159,91,83
|
|
};
|
|
|
|
static float img_emboss[FILTER_SIZE][FILTER_SIZE] =
|
|
{
|
|
{-0.7f, -0.7f, -0.7f, -0.7f, 0.0f },
|
|
{-0.7f, -0.7f, -0.7f, 0.0f, 0.7f },
|
|
{-0.7f, -0.7f, 0.0f, 0.7f, 0.7f },
|
|
{-0.7f, 0.0f, 0.7f, 0.7f, 0.7f },
|
|
{ 0.0f, 0.7f, 0.7f, 0.7f, 0.7f },
|
|
};
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
XASH3D LOAD IMAGE FORMATS
|
|
|
|
=============================================================================
|
|
*/
|
|
// stub
|
|
static const loadpixformat_t load_null[] =
|
|
{
|
|
{ NULL, NULL, NULL, IL_HINT_NO }
|
|
};
|
|
|
|
static const loadpixformat_t load_game[] =
|
|
{
|
|
{ "%s%s.%s", "dds", Image_LoadDDS, IL_HINT_NO }, // dds for world and studio models
|
|
{ "%s%s.%s", "tga", Image_LoadTGA, IL_HINT_NO }, // hl vgui menus
|
|
{ "%s%s.%s", "bmp", Image_LoadBMP, IL_HINT_NO }, // WON menu images
|
|
{ "%s%s.%s", "png", Image_LoadPNG, IL_HINT_NO }, // NightFire 007 menus
|
|
{ "%s%s.%s", "mip", Image_LoadMIP, IL_HINT_NO }, // hl textures from wad or buffer
|
|
{ "%s%s.%s", "mdl", Image_LoadMDL, IL_HINT_HL }, // hl studio model skins
|
|
{ "%s%s.%s", "spr", Image_LoadSPR, IL_HINT_HL }, // hl sprite frames
|
|
{ "%s%s.%s", "lmp", Image_LoadLMP, IL_HINT_NO }, // hl menu images (cached.wad etc)
|
|
{ "%s%s.%s", "fnt", Image_LoadFNT, IL_HINT_HL }, // hl console font (fonts.wad etc)
|
|
{ "%s%s.%s", "pal", Image_LoadPAL, IL_HINT_NO }, // install studio\sprite palette
|
|
{ NULL, NULL, NULL, IL_HINT_NO }
|
|
};
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
XASH3D SAVE IMAGE FORMATS
|
|
|
|
=============================================================================
|
|
*/
|
|
// stub
|
|
static const savepixformat_t save_null[] =
|
|
{
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
|
|
// Xash3D normal instance
|
|
static const savepixformat_t save_game[] =
|
|
{
|
|
{ "%s%s.%s", "tga", Image_SaveTGA }, // tga screenshots
|
|
{ "%s%s.%s", "bmp", Image_SaveBMP }, // bmp levelshots or screenshots
|
|
{ "%s%s.%s", "png", Image_SavePNG }, // png screenshots
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
|
|
void Image_Init( void )
|
|
{
|
|
// init pools
|
|
host.imagepool = Mem_AllocPool( "ImageLib Pool" );
|
|
|
|
// install image formats (can be re-install later by Image_Setup)
|
|
switch( host.type )
|
|
{
|
|
case HOST_NORMAL:
|
|
image.cmd_flags = IL_USE_LERPING|IL_ALLOW_OVERWRITE;
|
|
image.loadformats = load_game;
|
|
image.saveformats = save_game;
|
|
break;
|
|
case HOST_DEDICATED:
|
|
image.cmd_flags = 0;
|
|
image.loadformats = load_game;
|
|
image.saveformats = save_null;
|
|
break;
|
|
default: // all other instances not using imagelib
|
|
image.cmd_flags = 0;
|
|
image.loadformats = load_null;
|
|
image.saveformats = save_null;
|
|
break;
|
|
}
|
|
|
|
image.tempbuffer = NULL;
|
|
}
|
|
|
|
void Image_Shutdown( void )
|
|
{
|
|
Mem_Check(); // check for leaks
|
|
Mem_FreePool( &host.imagepool );
|
|
}
|
|
|
|
byte *Image_Copy( size_t size )
|
|
{
|
|
byte *out;
|
|
|
|
out = Mem_Malloc( host.imagepool, size );
|
|
memcpy( out, image.tempbuffer, size );
|
|
|
|
return out;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Image_CustomPalette
|
|
=================
|
|
*/
|
|
qboolean Image_CustomPalette( void )
|
|
{
|
|
return image.custom_palette;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Image_CheckFlag
|
|
=================
|
|
*/
|
|
qboolean Image_CheckFlag( int bit )
|
|
{
|
|
if( FBitSet( image.force_flags, bit ))
|
|
return true;
|
|
|
|
if( FBitSet( image.cmd_flags, bit ))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Image_SetForceFlags
|
|
=================
|
|
*/
|
|
void Image_SetForceFlags( uint flags )
|
|
{
|
|
SetBits( image.force_flags, flags );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Image_ClearForceFlags
|
|
=================
|
|
*/
|
|
void Image_ClearForceFlags( void )
|
|
{
|
|
image.force_flags = 0;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Image_AddCmdFlags
|
|
=================
|
|
*/
|
|
void Image_AddCmdFlags( uint flags )
|
|
{
|
|
SetBits( image.cmd_flags, flags );
|
|
}
|
|
|
|
qboolean Image_ValidSize( const char *name )
|
|
{
|
|
if( image.width > IMAGE_MAXWIDTH || image.height > IMAGE_MAXHEIGHT || image.width <= 0 || image.height <= 0 )
|
|
{
|
|
Con_DPrintf( S_ERROR "Image: (%s) dims out of range [%dx%d]\n", name, image.width, image.height );
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
qboolean Image_LumpValidSize( const char *name )
|
|
{
|
|
if( image.width > LUMP_MAXWIDTH || image.height > LUMP_MAXHEIGHT || image.width <= 0 || image.height <= 0 )
|
|
{
|
|
Con_DPrintf( S_ERROR "Image: (%s) dims out of range [%dx%d]\n", name, image.width,image.height );
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
Image_ComparePalette
|
|
=============
|
|
*/
|
|
int Image_ComparePalette( const byte *pal )
|
|
{
|
|
if( pal == NULL )
|
|
return PAL_INVALID;
|
|
else if( !memcmp( palette_q1, pal, 765 )) // last color was changed
|
|
return PAL_QUAKE1;
|
|
else if( !memcmp( palette_hl, pal, 765 ))
|
|
return PAL_HALFLIFE;
|
|
return PAL_CUSTOM;
|
|
}
|
|
|
|
void Image_SetPalette( const byte *pal, uint *d_table )
|
|
{
|
|
byte rgba[4];
|
|
int i;
|
|
|
|
// setup palette
|
|
switch( image.d_rendermode )
|
|
{
|
|
case LUMP_NORMAL:
|
|
for( i = 0; i < 256; i++ )
|
|
{
|
|
rgba[0] = pal[i*3+0];
|
|
rgba[1] = pal[i*3+1];
|
|
rgba[2] = pal[i*3+2];
|
|
rgba[3] = 0xFF;
|
|
d_table[i] = *(uint *)rgba;
|
|
}
|
|
break;
|
|
case LUMP_GRADIENT:
|
|
for( i = 0; i < 256; i++ )
|
|
{
|
|
rgba[0] = pal[765];
|
|
rgba[1] = pal[766];
|
|
rgba[2] = pal[767];
|
|
rgba[3] = i;
|
|
d_table[i] = *(uint *)rgba;
|
|
}
|
|
break;
|
|
case LUMP_MASKED:
|
|
for( i = 0; i < 255; i++ )
|
|
{
|
|
rgba[0] = pal[i*3+0];
|
|
rgba[1] = pal[i*3+1];
|
|
rgba[2] = pal[i*3+2];
|
|
rgba[3] = 0xFF;
|
|
d_table[i] = *(uint *)rgba;
|
|
}
|
|
d_table[255] = 0;
|
|
break;
|
|
case LUMP_EXTENDED:
|
|
for( i = 0; i < 256; i++ )
|
|
{
|
|
rgba[0] = pal[i*4+0];
|
|
rgba[1] = pal[i*4+1];
|
|
rgba[2] = pal[i*4+2];
|
|
rgba[3] = pal[i*4+3];
|
|
d_table[i] = *(uint *)rgba;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void Image_ConvertPalTo24bit( rgbdata_t *pic )
|
|
{
|
|
byte *pal32, *pal24;
|
|
byte *converted;
|
|
int i;
|
|
|
|
if( pic->type == PF_INDEXED_24 )
|
|
return; // does nothing
|
|
|
|
pal24 = converted = Mem_Malloc( host.imagepool, 768 );
|
|
pal32 = pic->palette;
|
|
|
|
for( i = 0; i < 256; i++, pal24 += 3, pal32 += 4 )
|
|
{
|
|
pal24[0] = pal32[0];
|
|
pal24[1] = pal32[1];
|
|
pal24[2] = pal32[2];
|
|
}
|
|
|
|
Mem_Free( pic->palette );
|
|
pic->palette = converted;
|
|
pic->type = PF_INDEXED_24;
|
|
}
|
|
|
|
void Image_CopyPalette32bit( void )
|
|
{
|
|
if( image.palette ) return; // already created ?
|
|
image.palette = Mem_Malloc( host.imagepool, 1024 );
|
|
memcpy( image.palette, image.d_currentpal, 1024 );
|
|
}
|
|
|
|
void Image_CheckPaletteQ1( void )
|
|
{
|
|
rgbdata_t *pic = FS_LoadImage( DEFAULT_INTERNAL_PALETTE, NULL, 0 );
|
|
|
|
if( pic && pic->size == 1024 )
|
|
{
|
|
Image_ConvertPalTo24bit( pic );
|
|
if( Image_ComparePalette( pic->palette ) == PAL_CUSTOM )
|
|
{
|
|
image.d_rendermode = LUMP_NORMAL;
|
|
Con_DPrintf( "custom quake palette detected\n" );
|
|
Image_SetPalette( pic->palette, d_8toQ1table );
|
|
d_8toQ1table[255] = 0; // 255 is transparent
|
|
image.custom_palette = true;
|
|
q1palette_init = true;
|
|
}
|
|
}
|
|
|
|
if( pic ) FS_FreeImage( pic );
|
|
}
|
|
|
|
void Image_GetPaletteQ1( void )
|
|
{
|
|
if( !q1palette_init )
|
|
{
|
|
image.d_rendermode = LUMP_NORMAL;
|
|
Image_SetPalette( palette_q1, d_8toQ1table );
|
|
d_8toQ1table[255] = 0; // 255 is transparent
|
|
q1palette_init = true;
|
|
}
|
|
|
|
image.d_rendermode = LUMP_QUAKE1;
|
|
image.d_currentpal = d_8toQ1table;
|
|
}
|
|
|
|
void Image_GetPaletteHL( void )
|
|
{
|
|
if( !hlpalette_init )
|
|
{
|
|
image.d_rendermode = LUMP_NORMAL;
|
|
Image_SetPalette( palette_hl, d_8toHLtable );
|
|
hlpalette_init = true;
|
|
}
|
|
|
|
image.d_rendermode = LUMP_HALFLIFE;
|
|
image.d_currentpal = d_8toHLtable;
|
|
}
|
|
|
|
void Image_GetPaletteBMP( const byte *pal )
|
|
{
|
|
image.d_rendermode = LUMP_EXTENDED;
|
|
|
|
if( pal )
|
|
{
|
|
Image_SetPalette( pal, d_8to24table );
|
|
image.d_currentpal = d_8to24table;
|
|
}
|
|
}
|
|
|
|
void Image_GetPaletteLMP( const byte *pal, int rendermode )
|
|
{
|
|
image.d_rendermode = rendermode;
|
|
|
|
if( pal )
|
|
{
|
|
Image_SetPalette( pal, d_8to24table );
|
|
image.d_currentpal = d_8to24table;
|
|
}
|
|
else
|
|
{
|
|
switch( rendermode )
|
|
{
|
|
case LUMP_QUAKE1:
|
|
Image_GetPaletteQ1();
|
|
break;
|
|
case LUMP_HALFLIFE:
|
|
Image_GetPaletteHL();
|
|
break;
|
|
default:
|
|
// defaulting to half-life palette
|
|
Image_GetPaletteHL();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Image_PaletteHueReplace( byte *palSrc, int newHue, int start, int end, int pal_size )
|
|
{
|
|
float r, g, b;
|
|
float maxcol, mincol;
|
|
float hue, val, sat;
|
|
int i;
|
|
|
|
hue = (float)(newHue * ( 360.0f / 255 ));
|
|
pal_size = bound( 3, pal_size, 4 );
|
|
|
|
for( i = start; i <= end; i++ )
|
|
{
|
|
r = palSrc[i*pal_size+0];
|
|
g = palSrc[i*pal_size+1];
|
|
b = palSrc[i*pal_size+2];
|
|
|
|
maxcol = max( max( r, g ), b ) / 255.0f;
|
|
mincol = min( min( r, g ), b ) / 255.0f;
|
|
|
|
if( maxcol == 0 ) continue;
|
|
|
|
val = maxcol;
|
|
sat = (maxcol - mincol) / maxcol;
|
|
|
|
mincol = val * (1.0f - sat);
|
|
|
|
if( hue <= 120.0f )
|
|
{
|
|
b = mincol;
|
|
if( hue < 60 )
|
|
{
|
|
r = val;
|
|
g = mincol + hue * (val - mincol) / (120.0f - hue);
|
|
}
|
|
else
|
|
{
|
|
g = val;
|
|
r = mincol + (120.0f - hue) * (val - mincol) / hue;
|
|
}
|
|
}
|
|
else if( hue <= 240.0f )
|
|
{
|
|
r = mincol;
|
|
if( hue < 180.0f )
|
|
{
|
|
g = val;
|
|
b = mincol + (hue - 120.0f) * (val - mincol) / (240.0f - hue);
|
|
}
|
|
else
|
|
{
|
|
b = val;
|
|
g = mincol + (240.0f - hue) * (val - mincol) / (hue - 120.0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g = mincol;
|
|
if( hue < 300.0f )
|
|
{
|
|
b = val;
|
|
r = mincol + (hue - 240.0f) * (val - mincol) / (360.0f - hue);
|
|
}
|
|
else
|
|
{
|
|
r = val;
|
|
b = mincol + (360.0f - hue) * (val - mincol) / (hue - 240.0f);
|
|
}
|
|
}
|
|
|
|
palSrc[i*pal_size+0] = (byte)(r * 255);
|
|
palSrc[i*pal_size+1] = (byte)(g * 255);
|
|
palSrc[i*pal_size+2] = (byte)(b * 255);
|
|
}
|
|
}
|
|
|
|
void Image_PaletteTranslate( byte *palSrc, int top, int bottom, int pal_size )
|
|
{
|
|
byte dst[256], src[256];
|
|
int i;
|
|
|
|
pal_size = bound( 3, pal_size, 4 );
|
|
for( i = 0; i < 256; i++ )
|
|
src[i] = i;
|
|
memcpy( dst, src, 256 );
|
|
|
|
if( top < 128 )
|
|
{
|
|
// the artists made some backwards ranges. sigh.
|
|
memcpy( dst + SHIRT_HUE_START, src + top, 16 );
|
|
}
|
|
else
|
|
{
|
|
for( i = 0; i < 16; i++ )
|
|
dst[SHIRT_HUE_START+i] = src[top + 15 - i];
|
|
}
|
|
|
|
if( bottom < 128 )
|
|
{
|
|
memcpy( dst + PANTS_HUE_START, src + bottom, 16 );
|
|
}
|
|
else
|
|
{
|
|
for( i = 0; i < 16; i++ )
|
|
dst[PANTS_HUE_START + i] = src[bottom + 15 - i];
|
|
}
|
|
|
|
// last color isn't changed
|
|
for( i = 0; i < 255; i++ )
|
|
{
|
|
palSrc[i*pal_size+0] = palette_q1[dst[i]*3+0];
|
|
palSrc[i*pal_size+1] = palette_q1[dst[i]*3+1];
|
|
palSrc[i*pal_size+2] = palette_q1[dst[i]*3+2];
|
|
}
|
|
}
|
|
|
|
void Image_CopyParms( rgbdata_t *src )
|
|
{
|
|
Image_Reset();
|
|
|
|
image.width = src->width;
|
|
image.height = src->height;
|
|
image.type = src->type;
|
|
image.flags = src->flags;
|
|
image.size = src->size;
|
|
image.palette = src->palette; // may be NULL
|
|
|
|
memcpy( image.fogParams, src->fogParams, sizeof( image.fogParams ));
|
|
}
|
|
|
|
/*
|
|
============
|
|
Image_Copy8bitRGBA
|
|
|
|
NOTE: must call Image_GetPaletteXXX before used
|
|
============
|
|
*/
|
|
qboolean Image_Copy8bitRGBA( const byte *in, byte *out, int pixels )
|
|
{
|
|
int *iout = (int *)out;
|
|
byte *fin = (byte *)in;
|
|
byte *col;
|
|
int i;
|
|
|
|
if( !in || !image.d_currentpal )
|
|
return false;
|
|
|
|
// this is a base image with luma - clear luma pixels
|
|
if( image.flags & IMAGE_HAS_LUMA )
|
|
{
|
|
for( i = 0; i < image.width * image.height; i++ )
|
|
fin[i] = fin[i] < 224 ? fin[i] : 0;
|
|
}
|
|
|
|
// check for color
|
|
for( i = 0; i < 256; i++ )
|
|
{
|
|
col = (byte *)&image.d_currentpal[i];
|
|
if( col[0] != col[1] || col[1] != col[2] )
|
|
{
|
|
image.flags |= IMAGE_HAS_COLOR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
while( pixels >= 8 )
|
|
{
|
|
iout[0] = image.d_currentpal[in[0]];
|
|
iout[1] = image.d_currentpal[in[1]];
|
|
iout[2] = image.d_currentpal[in[2]];
|
|
iout[3] = image.d_currentpal[in[3]];
|
|
iout[4] = image.d_currentpal[in[4]];
|
|
iout[5] = image.d_currentpal[in[5]];
|
|
iout[6] = image.d_currentpal[in[6]];
|
|
iout[7] = image.d_currentpal[in[7]];
|
|
|
|
in += 8;
|
|
iout += 8;
|
|
pixels -= 8;
|
|
}
|
|
|
|
if( pixels & 4 )
|
|
{
|
|
iout[0] = image.d_currentpal[in[0]];
|
|
iout[1] = image.d_currentpal[in[1]];
|
|
iout[2] = image.d_currentpal[in[2]];
|
|
iout[3] = image.d_currentpal[in[3]];
|
|
in += 4;
|
|
iout += 4;
|
|
}
|
|
|
|
if( pixels & 2 )
|
|
{
|
|
iout[0] = image.d_currentpal[in[0]];
|
|
iout[1] = image.d_currentpal[in[1]];
|
|
in += 2;
|
|
iout += 2;
|
|
}
|
|
|
|
if( pixels & 1 ) // last byte
|
|
iout[0] = image.d_currentpal[in[0]];
|
|
image.type = PF_RGBA_32; // update image type;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void Image_Resample32LerpLine( const byte *in, byte *out, int inwidth, int outwidth )
|
|
{
|
|
int j, xi, oldx = 0, f, fstep, endx, lerp;
|
|
|
|
fstep = (int)(inwidth * 65536.0f / outwidth);
|
|
endx = (inwidth-1);
|
|
|
|
for( j = 0, f = 0; j < outwidth; j++, f += fstep )
|
|
{
|
|
xi = f>>16;
|
|
if( xi != oldx )
|
|
{
|
|
in += (xi - oldx) * 4;
|
|
oldx = xi;
|
|
}
|
|
if( xi < endx )
|
|
{
|
|
lerp = f & 0xFFFF;
|
|
*out++ = (byte)((((in[4] - in[0]) * lerp)>>16) + in[0]);
|
|
*out++ = (byte)((((in[5] - in[1]) * lerp)>>16) + in[1]);
|
|
*out++ = (byte)((((in[6] - in[2]) * lerp)>>16) + in[2]);
|
|
*out++ = (byte)((((in[7] - in[3]) * lerp)>>16) + in[3]);
|
|
}
|
|
else // last pixel of the line has no pixel to lerp to
|
|
{
|
|
*out++ = in[0];
|
|
*out++ = in[1];
|
|
*out++ = in[2];
|
|
*out++ = in[3];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void Image_Resample24LerpLine( const byte *in, byte *out, int inwidth, int outwidth )
|
|
{
|
|
int j, xi, oldx = 0, f, fstep, endx, lerp;
|
|
|
|
fstep = (int)(inwidth * 65536.0f / outwidth);
|
|
endx = (inwidth-1);
|
|
|
|
for( j = 0, f = 0; j < outwidth; j++, f += fstep )
|
|
{
|
|
xi = f>>16;
|
|
|
|
if( xi != oldx )
|
|
{
|
|
in += (xi - oldx) * 3;
|
|
oldx = xi;
|
|
}
|
|
|
|
if( xi < endx )
|
|
{
|
|
lerp = f & 0xFFFF;
|
|
*out++ = (byte)((((in[3] - in[0]) * lerp)>>16) + in[0]);
|
|
*out++ = (byte)((((in[4] - in[1]) * lerp)>>16) + in[1]);
|
|
*out++ = (byte)((((in[5] - in[2]) * lerp)>>16) + in[2]);
|
|
}
|
|
else // last pixel of the line has no pixel to lerp to
|
|
{
|
|
*out++ = in[0];
|
|
*out++ = in[1];
|
|
*out++ = in[2];
|
|
}
|
|
}
|
|
}
|
|
|
|
void Image_Resample32Lerp( const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight )
|
|
{
|
|
const byte *inrow;
|
|
int i, j, r, yi, oldy = 0, f, fstep, lerp, endy = (inheight - 1);
|
|
int inwidth4 = inwidth * 4;
|
|
int outwidth4 = outwidth * 4;
|
|
byte *out = (byte *)outdata;
|
|
byte *resamplerow1;
|
|
byte *resamplerow2;
|
|
|
|
fstep = (int)(inheight * 65536.0f / outheight);
|
|
|
|
resamplerow1 = (byte *)Mem_Malloc( host.imagepool, outwidth * 4 * 2);
|
|
resamplerow2 = resamplerow1 + outwidth * 4;
|
|
|
|
inrow = (const byte *)indata;
|
|
|
|
Image_Resample32LerpLine( inrow, resamplerow1, inwidth, outwidth );
|
|
Image_Resample32LerpLine( inrow + inwidth4, resamplerow2, inwidth, outwidth );
|
|
|
|
for( i = 0, f = 0; i < outheight; i++, f += fstep )
|
|
{
|
|
yi = f>>16;
|
|
|
|
if( yi < endy )
|
|
{
|
|
lerp = f & 0xFFFF;
|
|
if( yi != oldy )
|
|
{
|
|
inrow = (byte *)indata + inwidth4 * yi;
|
|
if( yi == oldy + 1 ) memcpy( resamplerow1, resamplerow2, outwidth4 );
|
|
else Image_Resample32LerpLine( inrow, resamplerow1, inwidth, outwidth );
|
|
Image_Resample32LerpLine( inrow + inwidth4, resamplerow2, inwidth, outwidth );
|
|
oldy = yi;
|
|
}
|
|
|
|
j = outwidth - 4;
|
|
|
|
while( j >= 0 )
|
|
{
|
|
LERPBYTE( 0);
|
|
LERPBYTE( 1);
|
|
LERPBYTE( 2);
|
|
LERPBYTE( 3);
|
|
LERPBYTE( 4);
|
|
LERPBYTE( 5);
|
|
LERPBYTE( 6);
|
|
LERPBYTE( 7);
|
|
LERPBYTE( 8);
|
|
LERPBYTE( 9);
|
|
LERPBYTE(10);
|
|
LERPBYTE(11);
|
|
LERPBYTE(12);
|
|
LERPBYTE(13);
|
|
LERPBYTE(14);
|
|
LERPBYTE(15);
|
|
out += 16;
|
|
resamplerow1 += 16;
|
|
resamplerow2 += 16;
|
|
j -= 4;
|
|
}
|
|
|
|
if( j & 2 )
|
|
{
|
|
LERPBYTE( 0);
|
|
LERPBYTE( 1);
|
|
LERPBYTE( 2);
|
|
LERPBYTE( 3);
|
|
LERPBYTE( 4);
|
|
LERPBYTE( 5);
|
|
LERPBYTE( 6);
|
|
LERPBYTE( 7);
|
|
out += 8;
|
|
resamplerow1 += 8;
|
|
resamplerow2 += 8;
|
|
}
|
|
|
|
if( j & 1 )
|
|
{
|
|
LERPBYTE( 0);
|
|
LERPBYTE( 1);
|
|
LERPBYTE( 2);
|
|
LERPBYTE( 3);
|
|
out += 4;
|
|
resamplerow1 += 4;
|
|
resamplerow2 += 4;
|
|
}
|
|
|
|
resamplerow1 -= outwidth4;
|
|
resamplerow2 -= outwidth4;
|
|
}
|
|
else
|
|
{
|
|
if( yi != oldy )
|
|
{
|
|
inrow = (byte *)indata + inwidth4 * yi;
|
|
if( yi == oldy + 1 ) memcpy( resamplerow1, resamplerow2, outwidth4 );
|
|
else Image_Resample32LerpLine( inrow, resamplerow1, inwidth, outwidth);
|
|
oldy = yi;
|
|
}
|
|
|
|
memcpy( out, resamplerow1, outwidth4 );
|
|
}
|
|
}
|
|
|
|
Mem_Free( resamplerow1 );
|
|
}
|
|
|
|
void Image_Resample32Nolerp( const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight )
|
|
{
|
|
int i, j;
|
|
uint frac, fracstep;
|
|
int *inrow, *out = (int *)outdata; // relies on int being 4 bytes
|
|
|
|
fracstep = inwidth * 0x10000 / outwidth;
|
|
|
|
for( i = 0; i < outheight; i++)
|
|
{
|
|
inrow = (int *)indata + inwidth * (i * inheight / outheight);
|
|
frac = fracstep>>1;
|
|
j = outwidth - 4;
|
|
|
|
while( j >= 0 )
|
|
{
|
|
out[0] = inrow[frac >> 16];frac += fracstep;
|
|
out[1] = inrow[frac >> 16];frac += fracstep;
|
|
out[2] = inrow[frac >> 16];frac += fracstep;
|
|
out[3] = inrow[frac >> 16];frac += fracstep;
|
|
out += 4;
|
|
j -= 4;
|
|
}
|
|
|
|
if( j & 2 )
|
|
{
|
|
out[0] = inrow[frac >> 16];frac += fracstep;
|
|
out[1] = inrow[frac >> 16];frac += fracstep;
|
|
out += 2;
|
|
}
|
|
|
|
if( j & 1 )
|
|
{
|
|
out[0] = inrow[frac >> 16];frac += fracstep;
|
|
out += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Image_Resample24Lerp( const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight )
|
|
{
|
|
const byte *inrow;
|
|
int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight - 1);
|
|
int inwidth3 = inwidth * 3;
|
|
int outwidth3 = outwidth * 3;
|
|
byte *out = (byte *)outdata;
|
|
byte *resamplerow1;
|
|
byte *resamplerow2;
|
|
|
|
fstep = (int)(inheight * 65536.0f / outheight);
|
|
|
|
resamplerow1 = (byte *)Mem_Malloc( host.imagepool, outwidth * 3 * 2 );
|
|
resamplerow2 = resamplerow1 + outwidth*3;
|
|
|
|
inrow = (const byte *)indata;
|
|
oldy = 0;
|
|
Image_Resample24LerpLine( inrow, resamplerow1, inwidth, outwidth );
|
|
Image_Resample24LerpLine( inrow + inwidth3, resamplerow2, inwidth, outwidth );
|
|
|
|
for( i = 0, f = 0; i < outheight; i++, f += fstep )
|
|
{
|
|
yi = f>>16;
|
|
|
|
if( yi < endy )
|
|
{
|
|
lerp = f & 0xFFFF;
|
|
if( yi != oldy )
|
|
{
|
|
inrow = (byte *)indata + inwidth3 * yi;
|
|
if( yi == oldy + 1) memcpy( resamplerow1, resamplerow2, outwidth3 );
|
|
else Image_Resample24LerpLine( inrow, resamplerow1, inwidth, outwidth );
|
|
Image_Resample24LerpLine( inrow + inwidth3, resamplerow2, inwidth, outwidth );
|
|
oldy = yi;
|
|
}
|
|
|
|
j = outwidth - 4;
|
|
|
|
while( j >= 0 )
|
|
{
|
|
LERPBYTE( 0);
|
|
LERPBYTE( 1);
|
|
LERPBYTE( 2);
|
|
LERPBYTE( 3);
|
|
LERPBYTE( 4);
|
|
LERPBYTE( 5);
|
|
LERPBYTE( 6);
|
|
LERPBYTE( 7);
|
|
LERPBYTE( 8);
|
|
LERPBYTE( 9);
|
|
LERPBYTE(10);
|
|
LERPBYTE(11);
|
|
out += 12;
|
|
resamplerow1 += 12;
|
|
resamplerow2 += 12;
|
|
j -= 4;
|
|
}
|
|
|
|
if( j & 2 )
|
|
{
|
|
LERPBYTE( 0);
|
|
LERPBYTE( 1);
|
|
LERPBYTE( 2);
|
|
LERPBYTE( 3);
|
|
LERPBYTE( 4);
|
|
LERPBYTE( 5);
|
|
out += 6;
|
|
resamplerow1 += 6;
|
|
resamplerow2 += 6;
|
|
}
|
|
|
|
if( j & 1 )
|
|
{
|
|
LERPBYTE( 0);
|
|
LERPBYTE( 1);
|
|
LERPBYTE( 2);
|
|
out += 3;
|
|
resamplerow1 += 3;
|
|
resamplerow2 += 3;
|
|
}
|
|
|
|
resamplerow1 -= outwidth3;
|
|
resamplerow2 -= outwidth3;
|
|
}
|
|
else
|
|
{
|
|
if( yi != oldy )
|
|
{
|
|
inrow = (byte *)indata + inwidth3*yi;
|
|
if( yi == oldy + 1) memcpy( resamplerow1, resamplerow2, outwidth3 );
|
|
else Image_Resample24LerpLine( inrow, resamplerow1, inwidth, outwidth );
|
|
oldy = yi;
|
|
}
|
|
|
|
memcpy( out, resamplerow1, outwidth3 );
|
|
}
|
|
}
|
|
|
|
Mem_Free( resamplerow1 );
|
|
}
|
|
|
|
void Image_Resample24Nolerp( const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight )
|
|
{
|
|
uint frac, fracstep;
|
|
int i, j, f, inwidth3 = inwidth * 3;
|
|
byte *inrow, *out = (byte *)outdata;
|
|
|
|
fracstep = inwidth * 0x10000 / outwidth;
|
|
|
|
for( i = 0; i < outheight; i++)
|
|
{
|
|
inrow = (byte *)indata + inwidth3 * (i * inheight / outheight);
|
|
frac = fracstep>>1;
|
|
j = outwidth - 4;
|
|
|
|
while( j >= 0 )
|
|
{
|
|
f = (frac >> 16)*3;
|
|
*out++ = inrow[f+0];
|
|
*out++ = inrow[f+1];
|
|
*out++ = inrow[f+2];
|
|
frac += fracstep;
|
|
f = (frac >> 16)*3;
|
|
*out++ = inrow[f+0];
|
|
*out++ = inrow[f+1];
|
|
*out++ = inrow[f+2];
|
|
frac += fracstep;
|
|
f = (frac >> 16)*3;
|
|
*out++ = inrow[f+0];
|
|
*out++ = inrow[f+1];
|
|
*out++ = inrow[f+2];
|
|
frac += fracstep;
|
|
f = (frac >> 16)*3;
|
|
*out++ = inrow[f+0];
|
|
*out++ = inrow[f+1];
|
|
*out++ = inrow[f+2];
|
|
frac += fracstep;
|
|
j -= 4;
|
|
}
|
|
|
|
if( j & 2 )
|
|
{
|
|
f = (frac >> 16)*3;
|
|
*out++ = inrow[f+0];
|
|
*out++ = inrow[f+1];
|
|
*out++ = inrow[f+2];
|
|
frac += fracstep;
|
|
f = (frac >> 16)*3;
|
|
*out++ = inrow[f+0];
|
|
*out++ = inrow[f+1];
|
|
*out++ = inrow[f+2];
|
|
frac += fracstep;
|
|
out += 2;
|
|
}
|
|
|
|
if( j & 1 )
|
|
{
|
|
f = (frac >> 16)*3;
|
|
*out++ = inrow[f+0];
|
|
*out++ = inrow[f+1];
|
|
*out++ = inrow[f+2];
|
|
frac += fracstep;
|
|
out += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Image_Resample8Nolerp( const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight )
|
|
{
|
|
int i, j;
|
|
byte *in, *inrow;
|
|
uint frac, fracstep;
|
|
byte *out = (byte *)outdata;
|
|
|
|
in = (byte *)indata;
|
|
fracstep = inwidth * 0x10000 / outwidth;
|
|
|
|
for( i = 0; i < outheight; i++, out += outwidth )
|
|
{
|
|
inrow = in + inwidth*(i*inheight/outheight);
|
|
frac = fracstep>>1;
|
|
|
|
for( j = 0; j < outwidth; j++ )
|
|
{
|
|
out[j] = inrow[frac>>16];
|
|
frac += fracstep;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
Image_Resample
|
|
================
|
|
*/
|
|
byte *Image_ResampleInternal( const void *indata, int inwidth, int inheight, int outwidth, int outheight, int type, qboolean *resampled )
|
|
{
|
|
qboolean quality = Image_CheckFlag( IL_USE_LERPING );
|
|
|
|
// nothing to resample ?
|
|
if( inwidth == outwidth && inheight == outheight )
|
|
{
|
|
*resampled = false;
|
|
return (byte *)indata;
|
|
}
|
|
|
|
// alloc new buffer
|
|
switch( type )
|
|
{
|
|
case PF_INDEXED_24:
|
|
case PF_INDEXED_32:
|
|
image.tempbuffer = (byte *)Mem_Realloc( host.imagepool, image.tempbuffer, outwidth * outheight );
|
|
Image_Resample8Nolerp( indata, inwidth, inheight, image.tempbuffer, outwidth, outheight );
|
|
break;
|
|
case PF_RGB_24:
|
|
case PF_BGR_24:
|
|
image.tempbuffer = (byte *)Mem_Realloc( host.imagepool, image.tempbuffer, outwidth * outheight * 3 );
|
|
if( quality ) Image_Resample24Lerp( indata, inwidth, inheight, image.tempbuffer, outwidth, outheight );
|
|
else Image_Resample24Nolerp( indata, inwidth, inheight, image.tempbuffer, outwidth, outheight );
|
|
break;
|
|
case PF_RGBA_32:
|
|
case PF_BGRA_32:
|
|
image.tempbuffer = (byte *)Mem_Realloc( host.imagepool, image.tempbuffer, outwidth * outheight * 4 );
|
|
if( quality ) Image_Resample32Lerp( indata, inwidth, inheight, image.tempbuffer, outwidth, outheight );
|
|
else Image_Resample32Nolerp( indata, inwidth, inheight, image.tempbuffer, outwidth, outheight );
|
|
break;
|
|
default:
|
|
*resampled = false;
|
|
return (byte *)indata;
|
|
}
|
|
|
|
*resampled = true;
|
|
return image.tempbuffer;
|
|
}
|
|
|
|
/*
|
|
================
|
|
Image_Flip
|
|
================
|
|
*/
|
|
byte *Image_FlipInternal( const byte *in, word *srcwidth, word *srcheight, int type, int flags )
|
|
{
|
|
int i, x, y;
|
|
word width = *srcwidth;
|
|
word height = *srcheight;
|
|
int samples = PFDesc[type].bpp;
|
|
qboolean flip_x = FBitSet( flags, IMAGE_FLIP_X ) ? true : false;
|
|
qboolean flip_y = FBitSet( flags, IMAGE_FLIP_Y ) ? true : false;
|
|
qboolean flip_i = FBitSet( flags, IMAGE_ROT_90 ) ? true : false;
|
|
int row_inc = ( flip_y ? -samples : samples ) * width;
|
|
int col_inc = ( flip_x ? -samples : samples );
|
|
int row_ofs = ( flip_y ? ( height - 1 ) * width * samples : 0 );
|
|
int col_ofs = ( flip_x ? ( width - 1 ) * samples : 0 );
|
|
const byte *p, *line;
|
|
byte *out;
|
|
|
|
// nothing to process
|
|
if( !FBitSet( flags, IMAGE_FLIP_X|IMAGE_FLIP_Y|IMAGE_ROT_90 ))
|
|
return (byte *)in;
|
|
|
|
switch( type )
|
|
{
|
|
case PF_INDEXED_24:
|
|
case PF_INDEXED_32:
|
|
case PF_RGB_24:
|
|
case PF_BGR_24:
|
|
case PF_RGBA_32:
|
|
case PF_BGRA_32:
|
|
image.tempbuffer = Mem_Realloc( host.imagepool, image.tempbuffer, width * height * samples );
|
|
break;
|
|
default:
|
|
return (byte *)in;
|
|
}
|
|
|
|
out = image.tempbuffer;
|
|
|
|
if( flip_i )
|
|
{
|
|
for( x = 0, line = in + col_ofs; x < width; x++, line += col_inc )
|
|
for( y = 0, p = line + row_ofs; y < height; y++, p += row_inc, out += samples )
|
|
for( i = 0; i < samples; i++ )
|
|
out[i] = p[i];
|
|
}
|
|
else
|
|
{
|
|
for( y = 0, line = in + row_ofs; y < height; y++, line += row_inc )
|
|
for( x = 0, p = line + col_ofs; x < width; x++, p += col_inc, out += samples )
|
|
for( i = 0; i < samples; i++ )
|
|
out[i] = p[i];
|
|
}
|
|
|
|
// update dims
|
|
if( FBitSet( flags, IMAGE_ROT_90 ))
|
|
{
|
|
*srcwidth = height;
|
|
*srcheight = width;
|
|
}
|
|
else
|
|
{
|
|
*srcwidth = width;
|
|
*srcheight = height;
|
|
}
|
|
|
|
return image.tempbuffer;
|
|
}
|
|
|
|
byte *Image_CreateLumaInternal( byte *fin, int width, int height, int type, int flags )
|
|
{
|
|
byte *out;
|
|
int i;
|
|
|
|
if( !FBitSet( flags, IMAGE_HAS_LUMA ))
|
|
return (byte *)fin;
|
|
|
|
switch( type )
|
|
{
|
|
case PF_INDEXED_24:
|
|
case PF_INDEXED_32:
|
|
out = image.tempbuffer = Mem_Realloc( host.imagepool, image.tempbuffer, width * height );
|
|
for( i = 0; i < width * height; i++ )
|
|
*out++ = fin[i] >= 224 ? fin[i] : 0;
|
|
break;
|
|
default:
|
|
// another formats does ugly result :(
|
|
Con_Printf( S_ERROR "Image_MakeLuma: unsupported format %s\n", PFDesc[type].name );
|
|
return (byte *)fin;
|
|
}
|
|
|
|
return image.tempbuffer;
|
|
}
|
|
|
|
qboolean Image_AddIndexedImageToPack( const byte *in, int width, int height )
|
|
{
|
|
int mipsize = width * height;
|
|
qboolean expand_to_rgba = true;
|
|
|
|
if( Image_CheckFlag( IL_KEEP_8BIT ))
|
|
expand_to_rgba = false;
|
|
else if( FBitSet( image.flags, IMAGE_HAS_LUMA|IMAGE_QUAKESKY ))
|
|
expand_to_rgba = false;
|
|
|
|
image.size = mipsize;
|
|
|
|
if( expand_to_rgba ) image.size *= 4;
|
|
else Image_CopyPalette32bit();
|
|
|
|
// reallocate image buffer
|
|
image.rgba = Mem_Malloc( host.imagepool, image.size );
|
|
if( !expand_to_rgba ) memcpy( image.rgba, in, image.size );
|
|
else if( !Image_Copy8bitRGBA( in, image.rgba, mipsize ))
|
|
return false; // probably pallette not installed
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
Image_Decompress
|
|
|
|
force to unpack any image to 32-bit buffer
|
|
=============
|
|
*/
|
|
qboolean Image_Decompress( const byte *data )
|
|
{
|
|
byte *fin, *fout;
|
|
int i, size;
|
|
|
|
if( !data ) return false;
|
|
fin = (byte *)data;
|
|
|
|
size = image.width * image.height * 4;
|
|
image.tempbuffer = Mem_Realloc( host.imagepool, image.tempbuffer, size );
|
|
fout = image.tempbuffer;
|
|
|
|
switch( PFDesc[image.type].format )
|
|
{
|
|
case PF_INDEXED_24:
|
|
if( image.flags & IMAGE_HAS_ALPHA )
|
|
{
|
|
if( image.flags & IMAGE_COLORINDEX )
|
|
Image_GetPaletteLMP( image.palette, LUMP_GRADIENT );
|
|
else Image_GetPaletteLMP( image.palette, LUMP_MASKED );
|
|
}
|
|
else Image_GetPaletteLMP( image.palette, LUMP_NORMAL );
|
|
// intentionally fallthrough
|
|
case PF_INDEXED_32:
|
|
if( !image.d_currentpal ) image.d_currentpal = (uint *)image.palette;
|
|
if( !Image_Copy8bitRGBA( fin, fout, image.width * image.height ))
|
|
return false;
|
|
break;
|
|
case PF_BGR_24:
|
|
for (i = 0; i < image.width * image.height; i++ )
|
|
{
|
|
fout[(i<<2)+0] = fin[i*3+2];
|
|
fout[(i<<2)+1] = fin[i*3+1];
|
|
fout[(i<<2)+2] = fin[i*3+0];
|
|
fout[(i<<2)+3] = 255;
|
|
}
|
|
break;
|
|
case PF_RGB_24:
|
|
for (i = 0; i < image.width * image.height; i++ )
|
|
{
|
|
fout[(i<<2)+0] = fin[i*3+0];
|
|
fout[(i<<2)+1] = fin[i*3+1];
|
|
fout[(i<<2)+2] = fin[i*3+2];
|
|
fout[(i<<2)+3] = 255;
|
|
}
|
|
break;
|
|
case PF_BGRA_32:
|
|
for( i = 0; i < image.width * image.height; i++ )
|
|
{
|
|
fout[i*4+0] = fin[i*4+2];
|
|
fout[i*4+1] = fin[i*4+1];
|
|
fout[i*4+2] = fin[i*4+0];
|
|
fout[i*4+3] = fin[i*4+3];
|
|
}
|
|
break;
|
|
case PF_RGBA_32:
|
|
// fast default case
|
|
memcpy( fout, fin, size );
|
|
break;
|
|
default: return false;
|
|
}
|
|
|
|
// set new size
|
|
image.size = size;
|
|
|
|
return true;
|
|
}
|
|
|
|
rgbdata_t *Image_DecompressInternal( rgbdata_t *pic )
|
|
{
|
|
// quick case to reject unneeded conversions
|
|
if( pic->type == PF_RGBA_32 )
|
|
return pic;
|
|
|
|
Image_CopyParms( pic );
|
|
image.size = image.ptr = 0;
|
|
|
|
Image_Decompress( pic->buffer );
|
|
|
|
// now we can change type to RGBA
|
|
pic->type = PF_RGBA_32;
|
|
|
|
pic->buffer = Mem_Realloc( host.imagepool, pic->buffer, image.size );
|
|
memcpy( pic->buffer, image.tempbuffer, image.size );
|
|
if( pic->palette ) Mem_Free( pic->palette );
|
|
pic->flags = image.flags;
|
|
pic->palette = NULL;
|
|
|
|
return pic;
|
|
}
|
|
|
|
rgbdata_t *Image_LightGamma( rgbdata_t *pic )
|
|
{
|
|
byte *in = (byte *)pic->buffer;
|
|
int i;
|
|
|
|
if( pic->type != PF_RGBA_32 )
|
|
return pic;
|
|
|
|
for( i = 0; i < pic->width * pic->height; i++, in += 4 )
|
|
{
|
|
in[0] = LightToTexGamma( in[0] );
|
|
in[1] = LightToTexGamma( in[1] );
|
|
in[2] = LightToTexGamma( in[2] );
|
|
}
|
|
|
|
return pic;
|
|
}
|
|
|
|
qboolean Image_RemapInternal( rgbdata_t *pic, int topColor, int bottomColor )
|
|
{
|
|
if( !pic->palette )
|
|
return false;
|
|
|
|
switch( pic->type )
|
|
{
|
|
case PF_INDEXED_24:
|
|
break;
|
|
case PF_INDEXED_32:
|
|
Image_ConvertPalTo24bit( pic );
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if( Image_ComparePalette( pic->palette ) == PAL_QUAKE1 )
|
|
{
|
|
Image_PaletteTranslate( pic->palette, topColor * 16, bottomColor * 16, 3 );
|
|
}
|
|
else
|
|
{
|
|
// g-cont. preview images has a swapped top and bottom colors. I don't know why.
|
|
Image_PaletteHueReplace( pic->palette, topColor, SUIT_HUE_START, SUIT_HUE_END, 3 );
|
|
Image_PaletteHueReplace( pic->palette, bottomColor, PLATE_HUE_START, PLATE_HUE_END, 3 );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Image_ApplyFilter
|
|
|
|
Applies a 5 x 5 filtering matrix to the texture, then runs it through a simulated OpenGL texture environment
|
|
blend with the original data to derive a new texture. Freaky, funky, and *f--king* *fantastic*. You can do
|
|
reasonable enough "fake bumpmapping" with this baby...
|
|
|
|
Filtering algorithm from http://www.student.kuleuven.ac.be/~m0216922/CG/filtering.html
|
|
All credit due
|
|
==================
|
|
*/
|
|
static void Image_ApplyFilter( rgbdata_t *pic, float factor )
|
|
{
|
|
int i, x, y;
|
|
uint *fin, *fout;
|
|
size_t size;
|
|
|
|
// don't waste time
|
|
if( factor <= 0.0f ) return;
|
|
|
|
// first expand the image into 32-bit buffer
|
|
pic = Image_DecompressInternal( pic );
|
|
factor = bound( 0.0f, factor, 1.0f );
|
|
size = image.width * image.height * 4;
|
|
image.tempbuffer = Mem_Realloc( host.imagepool, image.tempbuffer, size );
|
|
fout = (uint *)image.tempbuffer;
|
|
fin = (uint *)pic->buffer;
|
|
|
|
for( x = 0; x < image.width; x++ )
|
|
{
|
|
for( y = 0; y < image.height; y++ )
|
|
{
|
|
vec3_t vout = { 0.0f, 0.0f, 0.0f };
|
|
int pos_x, pos_y;
|
|
float avg;
|
|
|
|
for( pos_x = 0; pos_x < FILTER_SIZE; pos_x++ )
|
|
{
|
|
for( pos_y = 0; pos_y < FILTER_SIZE; pos_y++ )
|
|
{
|
|
int img_x = (x - (FILTER_SIZE / 2) + pos_x + image.width) % image.width;
|
|
int img_y = (y - (FILTER_SIZE / 2) + pos_y + image.height) % image.height;
|
|
|
|
// casting's a unary operation anyway, so the othermost set of brackets in the left part
|
|
// of the rvalue should not be necessary... but i'm paranoid when it comes to C...
|
|
vout[0] += ((float)((byte *)&fin[img_y * image.width + img_x])[0]) * img_emboss[pos_x][pos_y];
|
|
vout[1] += ((float)((byte *)&fin[img_y * image.width + img_x])[1]) * img_emboss[pos_x][pos_y];
|
|
vout[2] += ((float)((byte *)&fin[img_y * image.width + img_x])[2]) * img_emboss[pos_x][pos_y];
|
|
}
|
|
}
|
|
|
|
// multiply by factor, add bias, and clamp
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
vout[i] *= factor;
|
|
vout[i] += 128.0f; // base
|
|
vout[i] = bound( 0.0f, vout[i], 255.0f );
|
|
}
|
|
|
|
// NTSC greyscale conversion standard
|
|
avg = (vout[0] * 30.0f + vout[1] * 59.0f + vout[2] * 11.0f) / 100.0f;
|
|
|
|
// divide by 255 so GL operations work as expected
|
|
vout[0] = avg / 255.0f;
|
|
vout[1] = avg / 255.0f;
|
|
vout[2] = avg / 255.0f;
|
|
|
|
// write to temp - first, write data in (to get the alpha channel quickly and
|
|
// easily, which will be left well alone by this particular operation...!)
|
|
fout[y * image.width + x] = fin[y * image.width + x];
|
|
|
|
// now write in each element, applying the blend operator. blend
|
|
// operators are based on standard OpenGL TexEnv modes, and the
|
|
// formulas are derived from the OpenGL specs (http://www.opengl.org).
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
// divide by 255 so GL operations work as expected
|
|
float src = ((float)((byte *)&fin[y * image.width + x])[i]) / 255.0f;
|
|
float tmp;
|
|
|
|
// default is GL_BLEND here
|
|
// CsS + CdD works out as Src * Dst * 2
|
|
tmp = vout[i] * src * 2.0f;
|
|
|
|
// multiply back by 255 to get the proper byte scale
|
|
tmp *= 255.0f;
|
|
|
|
// bound the temp target again now, cos the operation may have thrown it out
|
|
tmp = bound( 0.0f, tmp, 255.0f );
|
|
// and copy it in
|
|
((byte *)&fout[y * image.width + x])[i] = (byte)tmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
// copy result back
|
|
memcpy( fin, fout, size );
|
|
}
|
|
|
|
qboolean Image_Process( rgbdata_t **pix, int width, int height, uint flags, float bumpscale )
|
|
{
|
|
rgbdata_t *pic = *pix;
|
|
qboolean result = true;
|
|
byte *out;
|
|
|
|
// check for buffers
|
|
if( !pic || !pic->buffer )
|
|
{
|
|
image.force_flags = 0;
|
|
return false;
|
|
}
|
|
|
|
if( !flags )
|
|
{
|
|
// clear any force flags
|
|
image.force_flags = 0;
|
|
return false; // no operation specfied
|
|
}
|
|
|
|
if( FBitSet( flags, IMAGE_MAKE_LUMA ))
|
|
{
|
|
out = Image_CreateLumaInternal( pic->buffer, pic->width, pic->height, pic->type, pic->flags );
|
|
if( pic->buffer != out ) memcpy( pic->buffer, image.tempbuffer, pic->size );
|
|
ClearBits( pic->flags, IMAGE_HAS_LUMA );
|
|
}
|
|
|
|
if( FBitSet( flags, IMAGE_REMAP ))
|
|
{
|
|
// NOTE: user should keep copy of indexed image manually for new changes
|
|
if( Image_RemapInternal( pic, width, height ))
|
|
pic = Image_DecompressInternal( pic );
|
|
}
|
|
|
|
// update format to RGBA if any
|
|
if( FBitSet( flags, IMAGE_FORCE_RGBA ))
|
|
pic = Image_DecompressInternal( pic );
|
|
|
|
if( FBitSet( flags, IMAGE_LIGHTGAMMA ))
|
|
pic = Image_LightGamma( pic );
|
|
|
|
if( FBitSet( flags, IMAGE_EMBOSS ))
|
|
Image_ApplyFilter( pic, bumpscale );
|
|
|
|
out = Image_FlipInternal( pic->buffer, &pic->width, &pic->height, pic->type, flags );
|
|
if( pic->buffer != out ) memcpy( pic->buffer, image.tempbuffer, pic->size );
|
|
|
|
if( FBitSet( flags, IMAGE_RESAMPLE ) && width > 0 && height > 0 )
|
|
{
|
|
int w = bound( 1, width, IMAGE_MAXWIDTH ); // 1 - 4096
|
|
int h = bound( 1, height, IMAGE_MAXHEIGHT); // 1 - 4096
|
|
qboolean resampled = false;
|
|
|
|
out = Image_ResampleInternal((uint *)pic->buffer, pic->width, pic->height, w, h, pic->type, &resampled );
|
|
|
|
if( resampled ) // resampled or filled
|
|
{
|
|
Con_Reportf( "Image_Resample: from[%d x %d] to [%d x %d]\n", pic->width, pic->height, w, h );
|
|
pic->width = w, pic->height = h;
|
|
pic->size = w * h * PFDesc[pic->type].bpp;
|
|
Mem_Free( pic->buffer ); // free original image buffer
|
|
pic->buffer = Image_Copy( pic->size ); // unzone buffer (don't touch image.tempbuffer)
|
|
}
|
|
else
|
|
{
|
|
// not a resampled or filled
|
|
result = false;
|
|
}
|
|
}
|
|
|
|
// quantize image
|
|
if( FBitSet( flags, IMAGE_QUANTIZE ))
|
|
pic = Image_Quantize( pic );
|
|
|
|
*pix = pic;
|
|
|
|
// clear any force flags
|
|
image.force_flags = 0;
|
|
|
|
return result;
|
|
}
|