2006-03-20 17:12:09 +00:00
/*
2014-06-01 17:55:23 +00:00
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING . txt .
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below .
* See COPYING . txt for license details .
2006-03-20 17:12:09 +00:00
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ( " PARALLAX " ) . PARALLAX , IN DISTRIBUTING THE CODE TO
END - USERS , AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN , GRANTS A
ROYALTY - FREE , PERPETUAL LICENSE TO SUCH END - USERS FOR USE BY SUCH END - USERS
IN USING , DISPLAYING , AND CREATING DERIVATIVE WORKS THEREOF , SO LONG AS
SUCH USE , DISPLAY OR CREATION IS FOR NON - COMMERCIAL , ROYALTY OR REVENUE
FREE PURPOSES . IN NO EVENT SHALL THE END - USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE - BEARING PURPOSES . THE END - USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE .
COPYRIGHT 1993 - 1998 PARALLAX SOFTWARE CORPORATION . ALL RIGHTS RESERVED .
*/
/*
2008-03-20 23:23:46 +00:00
*
2006-03-20 17:12:09 +00:00
* Graphical routines for setting the palette
*
*/
2013-12-17 22:14:43 +00:00
# include <algorithm>
2006-03-20 17:12:09 +00:00
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
2011-06-01 07:59:55 +00:00
# include "physfsx.h"
2006-03-20 17:12:09 +00:00
# include "pstypes.h"
# include "u_mem.h"
# include "gr.h"
2012-07-07 18:35:06 +00:00
# include "dxxerror.h"
2012-07-01 02:54:33 +00:00
# include "maths.h"
2006-03-20 17:12:09 +00:00
# include "palette.h"
2021-09-04 12:17:14 +00:00
# include "d_enumerate.h"
2013-01-06 21:11:53 +00:00
# include "dxxsconf.h"
2016-03-19 19:08:10 +00:00
# include "dsx-ns.h"
2013-01-06 21:11:53 +00:00
# include "compiler-range_for.h"
2022-12-31 16:21:47 +00:00
# include "partial_range.h"
2020-05-02 21:18:42 +00:00
# include <array>
2013-01-06 21:11:53 +00:00
2015-12-13 18:00:49 +00:00
namespace dcx {
2015-12-13 18:00:48 +00:00
2020-12-26 21:17:29 +00:00
namespace {
2021-08-26 03:13:45 +00:00
unsigned get_squared_color_delta ( const int r , const int g , const int b , const rgb_t & rgb )
{
const auto dr = r - rgb . r ;
const auto dg = g - rgb . g ;
const auto db = b - rgb . b ;
return ( dr * dr ) + ( dg * dg ) + ( db * db ) ;
}
2021-09-04 12:17:14 +00:00
color_palette_index gr_find_closest_color_palette ( const int r , const int g , const int b , const palette_array_t & palette )
{
// Anything is better than this.
unsigned best_value = UINT_MAX ;
color_palette_index best_index { } ;
// only go to 255, 'cause we dont want to check the transparent color.
2021-09-04 12:17:14 +00:00
for ( auto & & [ candidate_idx , candidate_rgb ] : enumerate ( unchecked_partial_range ( palette , palette . size ( ) - 1 ) ) )
2021-09-04 12:17:14 +00:00
{
const auto candidate_value = get_squared_color_delta ( r , g , b , candidate_rgb ) ;
if ( best_value > candidate_value )
{
best_index = candidate_idx ;
if ( candidate_value = = 0 )
/* Perfect match */
break ;
best_value = candidate_value ;
}
}
return best_index ;
}
2014-09-20 23:47:27 +00:00
static unsigned Num_computed_colors ;
2006-03-20 17:12:09 +00:00
2013-12-22 22:03:07 +00:00
struct color_record {
2021-09-04 12:17:14 +00:00
uint8_t r , g , b ;
2020-07-16 02:31:04 +00:00
color_palette_index color_num ;
2013-12-22 22:03:07 +00:00
} ;
2006-03-20 17:12:09 +00:00
2020-12-27 22:03:09 +00:00
uint8_t gr_palette_gamma_param ;
2006-03-20 17:12:09 +00:00
2020-12-26 21:17:29 +00:00
}
2013-01-06 21:03:57 +00:00
palette_array_t gr_palette ;
palette_array_t gr_current_pal ;
2014-08-08 02:44:27 +00:00
gft_array1 gr_fade_table ;
2006-03-20 17:12:09 +00:00
ubyte gr_palette_gamma = 0 ;
2013-12-17 22:14:43 +00:00
void copy_bound_palette ( palette_array_t & d , const palette_array_t & s )
{
2013-01-06 21:11:53 +00:00
auto a = [ ] ( rgb_t c ) {
2021-09-04 12:17:14 +00:00
constexpr uint8_t bound { 63 } ;
2013-01-06 21:11:53 +00:00
c . r = std : : min ( c . r , bound ) ;
c . g = std : : min ( c . g , bound ) ;
c . b = std : : min ( c . b , bound ) ;
return c ;
} ;
std : : transform ( s . begin ( ) , s . end ( ) , d . begin ( ) , a ) ;
2013-12-17 22:14:43 +00:00
}
2015-12-22 04:18:50 +00:00
}
namespace dcx {
2020-12-26 21:17:29 +00:00
namespace {
2013-01-06 21:11:53 +00:00
static void diminish_entry ( rgb_t & c )
{
c . r > > = 2 ;
c . g > > = 2 ;
c . b > > = 2 ;
}
2020-12-26 21:17:29 +00:00
}
2013-12-17 22:14:43 +00:00
void diminish_palette ( palette_array_t & palette )
{
2013-01-06 21:11:53 +00:00
range_for ( rgb_t & c , palette )
diminish_entry ( c ) ;
2013-12-17 22:14:43 +00:00
}
2006-03-20 17:12:09 +00:00
void gr_palette_set_gamma ( int gamma )
{
if ( gamma < 0 ) gamma = 0 ;
if ( gamma > 16 ) gamma = 16 ; //was 8
if ( gr_palette_gamma_param ! = gamma ) {
gr_palette_gamma_param = gamma ;
gr_palette_gamma = gamma ;
2008-03-20 23:23:46 +00:00
gr_palette_load ( gr_palette ) ;
2006-03-20 17:12:09 +00:00
}
}
int gr_palette_get_gamma ( )
{
return gr_palette_gamma_param ;
}
2015-12-22 04:18:50 +00:00
}
namespace dsx {
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-01-06 21:03:57 +00:00
void gr_copy_palette ( palette_array_t & gr_palette , const palette_array_t & pal )
2006-03-20 17:12:09 +00:00
{
2013-01-06 21:11:53 +00:00
gr_palette = pal ;
2006-03-20 17:12:09 +00:00
Num_computed_colors = 0 ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2013-06-08 22:24:17 +00:00
void gr_use_palette_table ( const char * filename )
2006-03-20 17:12:09 +00:00
{
2014-08-08 02:44:27 +00:00
int fsize ;
2006-03-20 17:12:09 +00:00
2021-07-25 23:00:56 +00:00
auto & & [ fp , physfserr ] = PHYSFSX_openReadBuffered ( filename ) ;
if ( ! fp )
{
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2021-07-25 23:00:56 +00:00
Error ( " Failed to open palette file <%s>: %s " , filename , PHYSFS_getErrorByCode ( physfserr ) ) ;
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// the following is a hack to enable the loading of d2 levels
// even if only the d2 mac shareware datafiles are present.
// However, if the pig file is present but the palette file isn't,
// the textures in the level will look wierd...
2021-07-25 23:00:56 +00:00
auto & & [ fp2 , physfserr2 ] = PHYSFSX_openReadBuffered ( DEFAULT_LEVEL_PALETTE ) ;
if ( ! fp )
Error ( " Failed to open both palette file <%s> and default palette file < " DEFAULT_LEVEL_PALETTE " >: ( \" %s \" , \" %s \" ) " , filename , PHYSFS_getErrorByCode ( physfserr ) , PHYSFS_getErrorByCode ( physfserr2 ) ) ;
fp = std : : move ( fp2 ) ;
2013-03-03 01:03:33 +00:00
# endif
2021-07-25 23:00:56 +00:00
}
2006-03-20 17:12:09 +00:00
2011-06-01 07:59:55 +00:00
fsize = PHYSFS_fileLength ( fp ) ;
2006-03-20 17:12:09 +00:00
Assert ( fsize = = 9472 ) ;
2011-09-26 23:31:19 +00:00
( void ) fsize ;
2013-01-06 21:11:53 +00:00
PHYSFS_read ( fp , & gr_palette [ 0 ] , sizeof ( gr_palette [ 0 ] ) , gr_palette . size ( ) ) ;
2011-06-01 07:59:55 +00:00
PHYSFS_read ( fp , gr_fade_table , 256 * 34 , 1 ) ;
2015-01-17 18:31:42 +00:00
fp . reset ( ) ;
2006-03-20 17:12:09 +00:00
// This is the TRANSPARENCY COLOR
2014-08-08 02:44:27 +00:00
range_for ( auto & i , gr_fade_table )
i [ 255 ] = 255 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
Num_computed_colors = 0 ; // Flush palette cache.
// swap colors 0 and 255 of the palette along with fade table entries
# ifdef SWAP_0_255
for ( i = 0 ; i < 3 ; i + + ) {
2013-03-03 01:03:33 +00:00
ubyte c ;
2006-03-20 17:12:09 +00:00
c = gr_palette [ i ] ;
gr_palette [ i ] = gr_palette [ 765 + i ] ;
gr_palette [ 765 + i ] = c ;
}
for ( i = 0 ; i < GR_FADE_LEVELS * 256 ; i + + ) {
if ( gr_fade_table [ i ] = = 0 )
gr_fade_table [ i ] = 255 ;
}
for ( i = 0 ; i < GR_FADE_LEVELS ; i + + )
gr_fade_table [ i * 256 ] = TRANSPARENCY_COLOR ;
# endif
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2015-12-13 18:00:48 +00:00
}
2015-12-13 18:00:49 +00:00
namespace dcx {
2015-12-13 18:00:48 +00:00
2021-09-04 12:17:14 +00:00
void reset_computed_colors ( )
2006-03-20 17:12:09 +00:00
{
2021-09-04 12:17:14 +00:00
Num_computed_colors = 0 ;
2006-03-20 17:12:09 +00:00
}
2020-07-16 02:31:04 +00:00
color_palette_index gr_find_closest_color ( const int r , const int g , const int b )
2006-03-20 17:12:09 +00:00
{
2021-09-04 12:17:14 +00:00
static std : : array < color_record , 32 > Computed_colors ;
const auto num_computed_colors = Num_computed_colors ;
2006-03-20 17:12:09 +00:00
// If we've already computed this color, return it!
2021-09-04 12:17:14 +00:00
for ( unsigned i = 0 ; i < num_computed_colors ; + + i )
2015-04-26 20:15:57 +00:00
{
auto & c = Computed_colors [ i ] ;
if ( r = = c . r & & g = = c . g & & b = = c . b )
{
const auto color_num = c . color_num ;
2021-09-04 12:17:14 +00:00
if ( i )
{
2015-04-26 20:15:57 +00:00
std : : swap ( Computed_colors [ i - 1 ] , c ) ;
2006-03-20 17:12:09 +00:00
}
2015-04-26 20:15:56 +00:00
return color_num ;
2006-03-20 17:12:09 +00:00
}
2015-04-26 20:15:57 +00:00
}
2021-09-04 12:17:14 +00:00
// Add a computed color to list of computed colors in Computed_colors.
// If list wasn't full already, increment Num_computed_colors.
// If was full, replace the last one. Rely on the bubble-up logic
// above to move popular entries away from the end.
auto & cc = ( num_computed_colors < Computed_colors . size ( ) )
? Computed_colors [ Num_computed_colors + + ]
: Computed_colors . back ( ) ;
cc . r = r ;
cc . g = g ;
cc . b = b ;
return cc . color_num = gr_find_closest_color_palette ( r , g , b , gr_palette ) ;
2006-03-20 17:12:09 +00:00
}
2020-07-16 02:31:04 +00:00
color_palette_index gr_find_closest_color_15bpp ( int rgb )
2006-03-20 17:12:09 +00:00
{
return gr_find_closest_color ( ( ( rgb > > 10 ) & 31 ) * 2 , ( ( rgb > > 5 ) & 31 ) * 2 , ( rgb & 31 ) * 2 ) ;
}
2021-09-04 12:17:14 +00:00
color_palette_index gr_find_closest_color_current ( const int r , const int g , const int b )
2006-03-20 17:12:09 +00:00
{
2021-09-04 12:17:14 +00:00
return gr_find_closest_color_palette ( r , g , b , gr_current_pal ) ;
2006-03-20 17:12:09 +00:00
}
2015-12-13 18:00:48 +00:00
}