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 - 1999 PARALLAX SOFTWARE CORPORATION . ALL RIGHTS RESERVED .
*/
/*
*
* Graphical routines for drawing fonts .
*
*/
2015-10-09 02:46:09 +00:00
# include <algorithm>
2014-07-20 03:48:27 +00:00
# include <memory>
2014-07-20 17:16:02 +00:00
# include <stdexcept>
2006-03-20 17:12:09 +00:00
# include <stdarg.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
2010-07-01 09:03:52 +00:00
# ifndef macintosh
2006-03-20 17:12:09 +00:00
# include <fcntl.h>
# endif
# include "u_mem.h"
# include "gr.h"
2012-07-07 18:35:06 +00:00
# include "dxxerror.h"
2013-03-03 01:03:33 +00:00
# include "common/2d/bitmap.h"
2014-07-20 01:09:55 +00:00
# include "physfsx.h"
2008-02-24 14:41:27 +00:00
# include "gamefont.h"
2015-10-09 02:46:09 +00:00
# include "byteutil.h"
2008-04-06 20:23:28 +00:00
# include "console.h"
2010-09-02 13:55:28 +00:00
# include "config.h"
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2008-02-24 14:41:27 +00:00
# include "ogl_init.h"
# endif
2014-11-25 04:03:13 +00:00
# include "compiler-range_for.h"
# include "partial_range.h"
2021-09-04 12:17:14 +00:00
# include "d_range.h"
2020-05-02 21:18:42 +00:00
# include <array>
2020-05-02 21:18:43 +00:00
# include <memory>
2014-07-20 03:48:27 +00:00
2020-12-26 21:17:29 +00:00
namespace dcx {
namespace {
2016-10-15 00:53:15 +00:00
static font_x_scale_float FONTSCALE_X ( )
2016-02-10 03:58:05 +00:00
{
2021-09-19 10:53:48 +00:00
return font_x_scale_float ( FNTScaleX . operator float ( ) ) ;
2016-02-10 03:58:05 +00:00
}
2016-10-15 00:53:15 +00:00
static auto FONTSCALE_Y ( const int & y )
2016-02-10 03:58:05 +00:00
{
2021-09-19 10:53:48 +00:00
return font_y_scaled_float ( FNTScaleY * y ) ;
2016-02-10 03:58:05 +00:00
}
2006-03-20 17:12:09 +00:00
# define MAX_OPEN_FONTS 50
2017-10-14 17:10:30 +00:00
constexpr std : : integral_constant < uint8_t , 255 > kerndata_terminator { } ;
2015-11-14 18:17:21 +00:00
2006-03-20 17:12:09 +00:00
//list of open fonts, for use (for now) for palette remapping
2020-05-02 21:18:42 +00:00
static std : : array < grs_font * , MAX_OPEN_FONTS > open_font ;
2006-03-20 17:12:09 +00:00
# define BITS_TO_BYTES(x) (((x)+7)>>3)
2018-05-19 23:21:42 +00:00
static int gr_internal_string_clipped ( grs_canvas & , const grs_font & cv_font , int x , int y , const char * s ) ;
static int gr_internal_string_clipped_m ( grs_canvas & , const grs_font & cv_font , int x , int y , const char * s ) ;
2006-03-20 17:12:09 +00:00
2015-05-09 17:38:59 +00:00
static const uint8_t * find_kern_entry ( const grs_font & font , const uint8_t first , const uint8_t second )
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:38:59 +00:00
auto p = font . ft_kerndata ;
2006-03-20 17:12:09 +00:00
2015-11-14 18:17:21 +00:00
while ( * p ! = kerndata_terminator )
2006-03-20 17:12:09 +00:00
if ( p [ 0 ] = = first & & p [ 1 ] = = second )
return p ;
else p + = 3 ;
return NULL ;
}
//takes the character AFTER being offset into font
2010-07-01 09:03:52 +00:00
2016-02-12 04:02:28 +00:00
class font_character_extent
{
const unsigned r ;
public :
font_character_extent ( const grs_font & cv_font ) :
r ( cv_font . ft_maxchar - cv_font . ft_minchar )
{
}
bool operator ( ) ( const unsigned c ) const
{
return c < = r ;
}
} ;
2015-05-09 17:39:01 +00:00
template < typename T >
struct get_char_width_result
{
T width , spacing ;
} ;
/* Floating form never uses width. This specialization allows the
* compiler to recognize width as dead , shortening
* get_char_width < float > .
*/
template < >
struct get_char_width_result < float >
{
float spacing ;
get_char_width_result ( float , float s ) :
spacing ( s )
{
}
} ;
2015-05-09 17:38:59 +00:00
//takes the character BEFORE being offset into current font
template < typename T >
2015-12-22 04:18:50 +00:00
static get_char_width_result < T > get_char_width ( const grs_font & cv_font , const uint8_t c , const uint8_t c2 )
2010-07-01 09:03:52 +00:00
{
2015-05-09 17:38:59 +00:00
const unsigned letter = c - cv_font . ft_minchar ;
const auto ft_flags = cv_font . ft_flags ;
const auto proportional = ft_flags & FT_PROPORTIONAL ;
2010-07-01 09:03:52 +00:00
2016-02-10 03:58:05 +00:00
const auto & & fontscale_x = FONTSCALE_X ( ) ;
2016-02-12 04:02:28 +00:00
const auto & & INFONT = font_character_extent ( cv_font ) ;
2010-07-01 09:03:52 +00:00
if ( ! INFONT ( letter ) ) { //not in font, draw as space
2016-02-10 03:58:05 +00:00
return { 0 , static_cast < T > ( proportional ? fontscale_x ( cv_font . ft_w ) / 2 : cv_font . ft_w ) } ;
2006-03-20 17:12:09 +00:00
}
2018-06-12 02:53:06 +00:00
const T width = proportional ? fontscale_x ( cv_font . ft_widths [ letter ] ) . operator float ( ) : cv_font . ft_w ;
2015-05-09 17:38:59 +00:00
if ( ft_flags & FT_KERNED )
{
2006-03-20 17:12:09 +00:00
if ( ! ( c2 = = 0 | | c2 = = ' \n ' ) ) {
2015-05-09 17:38:59 +00:00
const unsigned letter2 = c2 - cv_font . ft_minchar ;
2008-02-27 22:05:58 +00:00
2006-03-20 17:12:09 +00:00
if ( INFONT ( letter2 ) ) {
2015-05-09 17:38:59 +00:00
const auto p = find_kern_entry ( cv_font , letter , letter2 ) ;
2006-03-20 17:12:09 +00:00
if ( p )
2016-02-10 03:58:05 +00:00
return { width , static_cast < T > ( fontscale_x ( p [ 2 ] ) ) } ;
2006-03-20 17:12:09 +00:00
}
}
}
2015-05-09 17:39:01 +00:00
return { width , width } ;
2006-03-20 17:12:09 +00:00
}
2017-02-11 21:42:44 +00:00
static int get_centered_x ( const grs_canvas & canvas , const grs_font & cv_font , const char * s )
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:39:01 +00:00
float w ;
2018-05-19 23:21:42 +00:00
for ( w = 0. ; const char c = * s ; + + s )
{
if ( c = = ' \n ' )
break ;
if ( c < = 0x06 )
{
if ( c < = 0x03 )
2006-03-20 17:12:09 +00:00
s + + ;
continue ; //skip color codes.
}
2018-05-19 23:21:42 +00:00
w + = get_char_width < float > ( cv_font , c , s [ 1 ] ) . spacing ;
2006-03-20 17:12:09 +00:00
}
2017-02-11 21:42:44 +00:00
return ( canvas . cv_bitmap . bm_w - w ) / 2 ;
2006-03-20 17:12:09 +00:00
}
2006-10-09 21:44:24 +00:00
//hack to allow color codes to be embedded in strings -MPM
//note we subtract one from color, since 255 is "transparent" so it'll never be used, and 0 would otherwise end the string.
//function must already have orig_color var set (or they could be passed as args...)
//perhaps some sort of recursive orig_color type thing would be better, but that would be way too much trouble for little gain
2017-10-14 17:10:30 +00:00
constexpr std : : integral_constant < int , 1 > gr_message_color_level { } ;
2021-09-04 12:17:14 +00:00
# define CHECK_EMBEDDED_COLORS() if (const char control_code = *text_ptr; control_code >= 0x01 && control_code <= 0x02) { \
2006-10-09 21:44:24 +00:00
text_ptr + + ; \
if ( * text_ptr ) { \
2021-09-04 12:17:14 +00:00
if ( gr_message_color_level > = control_code ) \
2021-09-04 12:17:14 +00:00
canvas . cv_font_fg_color = * text_ptr ; \
2006-10-09 21:44:24 +00:00
text_ptr + + ; \
} \
} \
2021-09-04 12:17:14 +00:00
else if ( control_code = = 0x03 ) \
2012-04-09 09:30:40 +00:00
{ \
underline = 1 ; \
text_ptr + + ; \
} \
2021-09-04 12:17:14 +00:00
else if ( control_code > = 0x04 & & control_code < = 0x06 ) { \
if ( gr_message_color_level > = control_code - 3 ) \
2021-09-04 12:17:14 +00:00
canvas . cv_font_fg_color = orig_color ; \
2006-10-09 21:44:24 +00:00
text_ptr + + ; \
}
2015-05-09 17:39:01 +00:00
template < bool masked_draws_background >
2018-05-19 23:21:42 +00:00
static int gr_internal_string0_template ( grs_canvas & canvas , const grs_font & cv_font , const int x , int y , const char * const s )
2006-03-20 17:12:09 +00:00
{
2016-02-12 04:02:28 +00:00
const auto & & INFONT = font_character_extent ( cv_font ) ;
2015-05-09 17:39:02 +00:00
const auto ft_flags = cv_font . ft_flags ;
const auto proportional = ft_flags & FT_PROPORTIONAL ;
2017-01-08 22:31:58 +00:00
const auto cv_font_bg_color = canvas . cv_font_bg_color ;
2006-03-20 17:12:09 +00:00
int skip_lines = 0 ;
2015-05-09 17:39:02 +00:00
unsigned int VideoOffset1 ;
2006-03-20 17:12:09 +00:00
2015-05-09 17:39:01 +00:00
//to allow easy reseting to default string color with colored strings -MPM
2017-01-08 22:31:58 +00:00
const auto orig_color = canvas . cv_font_fg_color ;
VideoOffset1 = y * canvas . cv_bitmap . bm_rowsize + x ;
2021-08-26 03:13:45 +00:00
for ( auto next_row = s ; next_row ; )
2006-03-20 17:12:09 +00:00
{
2021-08-26 03:13:45 +00:00
const auto text_ptr1 = std : : exchange ( next_row , nullptr ) ;
2006-03-20 17:12:09 +00:00
if ( x = = 0x8000 ) { //centered
2017-03-11 19:56:23 +00:00
int xx = get_centered_x ( canvas , cv_font , text_ptr1 ) ;
2017-01-08 22:31:58 +00:00
VideoOffset1 = y * canvas . cv_bitmap . bm_rowsize + xx ;
2006-03-20 17:12:09 +00:00
}
2015-05-09 17:39:02 +00:00
for ( int r = 0 ; r < cv_font . ft_h ; + + r )
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:39:02 +00:00
auto text_ptr = text_ptr1 ;
2006-03-20 17:12:09 +00:00
2015-05-09 17:39:02 +00:00
unsigned VideoOffset = VideoOffset1 ;
2006-03-20 17:12:09 +00:00
2015-05-09 17:39:02 +00:00
for ( ; const uint8_t c0 = * text_ptr ; + + text_ptr )
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:39:02 +00:00
if ( c0 = = ' \n ' )
2006-03-20 17:12:09 +00:00
{
next_row = & text_ptr [ 1 ] ;
break ;
}
2015-05-09 17:39:02 +00:00
if ( c0 = = CC_COLOR )
{
2017-01-08 22:31:58 +00:00
canvas . cv_font_fg_color = static_cast < uint8_t > ( * + + text_ptr ) ;
2006-03-20 17:12:09 +00:00
continue ;
}
2015-05-09 17:39:02 +00:00
if ( c0 = = CC_LSPACING )
{
skip_lines = * + + text_ptr - ' 0 ' ;
2006-03-20 17:12:09 +00:00
continue ;
}
2015-05-09 17:39:02 +00:00
auto underline = unlikely ( c0 = = CC_UNDERLINE )
2015-05-09 17:39:02 +00:00
? + + text_ptr , r = = cv_font . ft_baseline + 2 | | r = = cv_font . ft_baseline + 3
: 0 ;
2006-03-20 17:12:09 +00:00
2015-05-09 17:39:02 +00:00
const uint8_t c = * text_ptr ;
2015-12-22 04:18:50 +00:00
const auto & result = get_char_width < int > ( cv_font , c , text_ptr [ 1 ] ) ;
2015-05-09 17:39:01 +00:00
const auto & width = result . width ;
const auto & spacing = result . spacing ;
2006-03-20 17:12:09 +00:00
2015-05-09 17:39:02 +00:00
const unsigned letter = c - cv_font . ft_minchar ;
2006-03-20 17:12:09 +00:00
2020-07-16 02:31:04 +00:00
if constexpr ( masked_draws_background )
2014-11-25 04:03:13 +00:00
{
2020-07-16 02:31:04 +00:00
( void ) orig_color ;
2015-05-09 17:39:01 +00:00
if ( ! INFONT ( letter ) ) { //not in font, draw as space
VideoOffset + = spacing ;
text_ptr + + ;
continue ;
}
2014-11-25 04:03:13 +00:00
}
2006-03-20 17:12:09 +00:00
else
{
2015-05-09 17:39:02 +00:00
if ( ! INFONT ( letter ) | | c < = 0x06 ) //not in font, draw as space
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:39:01 +00:00
CHECK_EMBEDDED_COLORS ( ) else {
VideoOffset + = spacing ;
text_ptr + + ;
2006-03-20 17:12:09 +00:00
}
2015-05-09 17:39:01 +00:00
continue ;
2006-03-20 17:12:09 +00:00
}
}
2015-09-13 21:02:19 +00:00
if ( width )
{
2017-01-08 22:31:58 +00:00
auto data = & canvas . cv_bitmap . get_bitmap_data ( ) [ VideoOffset ] ;
const auto cv_font_fg_color = canvas . cv_font_fg_color ;
2006-03-20 17:12:09 +00:00
if ( underline )
2014-11-25 04:03:13 +00:00
{
2016-02-12 04:02:28 +00:00
std : : fill_n ( data , width , cv_font_fg_color ) ;
2014-11-25 04:03:13 +00:00
}
2006-03-20 17:12:09 +00:00
else
{
2015-05-09 17:39:02 +00:00
auto fp = proportional ? cv_font . ft_chars [ letter ] : & cv_font . ft_data [ letter * BITS_TO_BYTES ( width ) * cv_font . ft_h ] ;
2006-03-20 17:12:09 +00:00
fp + = BITS_TO_BYTES ( width ) * r ;
2015-09-13 21:02:19 +00:00
/* Setting bits=0 is a dead store, but is necessary to
* prevent - Og - Wuninitialized from issuing a bogus
* warning . - Og does not see that bits_remaining = 0
* guarantees that bits will be initialized from * fp before
* it is read .
*/
uint8_t bits_remaining = 0 , bits = 0 ;
for ( uint_fast32_t i = width ; i - - ; + + data , - - bits_remaining )
2006-03-20 17:12:09 +00:00
{
2015-09-13 21:02:19 +00:00
if ( ! bits_remaining )
{
2006-03-20 17:12:09 +00:00
bits = * fp + + ;
2015-09-13 21:02:19 +00:00
bits_remaining = 8 ;
2006-03-20 17:12:09 +00:00
}
2015-09-24 03:05:16 +00:00
const auto bit_enabled = ( bits & 0x80 ) ;
bits < < = 1 ;
2020-07-16 02:31:04 +00:00
if constexpr ( ! masked_draws_background )
2015-05-09 17:39:01 +00:00
{
2015-05-09 17:39:02 +00:00
if ( ! bit_enabled )
continue ;
2015-05-09 17:39:01 +00:00
}
2015-09-13 21:02:19 +00:00
* data = bit_enabled ? cv_font_fg_color : cv_font_bg_color ;
2006-03-20 17:12:09 +00:00
}
2015-09-13 21:02:19 +00:00
}
2006-03-20 17:12:09 +00:00
}
2016-02-12 04:02:28 +00:00
VideoOffset + = spacing ;
2006-03-20 17:12:09 +00:00
}
2017-01-08 22:31:58 +00:00
VideoOffset1 + = canvas . cv_bitmap . bm_rowsize ;
2006-03-20 17:12:09 +00:00
y + + ;
}
y + = skip_lines ;
2017-01-08 22:31:58 +00:00
VideoOffset1 + = canvas . cv_bitmap . bm_rowsize * skip_lines ;
2006-03-20 17:12:09 +00:00
skip_lines = 0 ;
}
return 0 ;
}
2018-05-19 23:21:42 +00:00
static int gr_internal_string0 ( grs_canvas & canvas , const grs_font & cv_font , const int x , const int y , const char * const s )
2015-05-09 17:39:01 +00:00
{
2018-05-19 23:21:42 +00:00
return gr_internal_string0_template < true > ( canvas , cv_font , x , y , s ) ;
2015-05-09 17:39:01 +00:00
}
2018-05-19 23:21:42 +00:00
static int gr_internal_string0m ( grs_canvas & canvas , const grs_font & cv_font , const int x , const int y , const char * const s )
2015-05-09 17:39:01 +00:00
{
2018-05-19 23:21:42 +00:00
return gr_internal_string0_template < false > ( canvas , cv_font , x , y , s ) ;
2015-05-09 17:39:01 +00:00
}
2016-09-24 18:06:11 +00:00
# if !DXX_USE_OGL
2021-08-26 03:13:45 +00:00
static void gr_internal_color_string ( grs_canvas & canvas , const grs_font & cv_font , const int x , const int y , const char * const s )
2013-07-27 16:28:04 +00:00
{
2006-03-20 17:12:09 +00:00
//a bitmap for the character
2014-08-08 02:52:56 +00:00
grs_bitmap char_bm = { } ;
2016-05-28 17:31:26 +00:00
char_bm . set_type ( bm_mode : : linear ) ;
2017-01-15 00:03:13 +00:00
char_bm . set_flags ( BM_FLAG_TRANSPARENT ) ;
2013-06-08 17:38:36 +00:00
const char * text_ptr , * next_row , * text_ptr1 ;
2015-05-09 17:39:01 +00:00
int letter ;
2006-03-20 17:12:09 +00:00
int xx , yy ;
2016-02-12 04:02:28 +00:00
const auto & & INFONT = font_character_extent ( cv_font ) ;
2015-12-22 04:18:50 +00:00
char_bm . bm_h = cv_font . ft_h ; //set height for chars of this font
2006-03-20 17:12:09 +00:00
next_row = s ;
yy = y ;
2016-02-10 03:58:05 +00:00
const auto & & fspacy1 = FSPACY ( 1 ) ;
2006-03-20 17:12:09 +00:00
while ( next_row ! = NULL )
{
text_ptr1 = next_row ;
next_row = NULL ;
text_ptr = text_ptr1 ;
xx = x ;
if ( xx = = 0x8000 ) //centered
2017-03-11 19:56:23 +00:00
xx = get_centered_x ( canvas , cv_font , text_ptr ) ;
2006-03-20 17:12:09 +00:00
while ( * text_ptr )
{
if ( * text_ptr = = ' \n ' )
{
next_row = & text_ptr [ 1 ] ;
2016-02-10 03:58:05 +00:00
yy + = cv_font . ft_h + fspacy1 ;
2006-03-20 17:12:09 +00:00
break ;
}
2016-12-29 03:27:08 +00:00
letter = static_cast < uint8_t > ( * text_ptr ) - cv_font . ft_minchar ;
2006-03-20 17:12:09 +00:00
2015-12-22 04:18:50 +00:00
const auto & result = get_char_width < int > ( cv_font , text_ptr [ 0 ] , text_ptr [ 1 ] ) ;
2015-05-09 17:39:01 +00:00
const auto & width = result . width ;
const auto & spacing = result . spacing ;
2006-03-20 17:12:09 +00:00
if ( ! INFONT ( letter ) ) { //not in font, draw as space
xx + = spacing ;
text_ptr + + ;
continue ;
}
2016-02-12 04:02:28 +00:00
const auto fp = ( cv_font . ft_flags & FT_PROPORTIONAL )
? cv_font . ft_chars [ letter ]
: & cv_font . ft_data [ letter * BITS_TO_BYTES ( width ) * cv_font . ft_h ] ;
2006-03-20 17:12:09 +00:00
2021-11-01 03:37:20 +00:00
/* Cast away const-ness on the font pointer. The function
* must take the parameter as non - const because some callers
* provide an uninitialized pointer and fill it later .
*/
gr_init_bitmap ( char_bm , bm_mode : : linear , 0 , 0 , width , cv_font . ft_h , width , const_cast < color_palette_index * > ( fp ) ) ;
2017-01-08 22:31:58 +00:00
gr_bitmapm ( canvas , xx , yy , char_bm ) ;
2006-03-20 17:12:09 +00:00
xx + = spacing ;
text_ptr + + ;
}
}
}
# else //OGL
2014-12-02 03:35:01 +00:00
static int get_font_total_width ( const grs_font & font )
{
if ( font . ft_flags & FT_PROPORTIONAL )
{
2014-11-25 04:03:13 +00:00
int w = 0 ;
2015-05-09 17:39:02 +00:00
range_for ( const auto v , unchecked_partial_range ( font . ft_widths , static_cast < unsigned > ( font . ft_maxchar - font . ft_minchar ) + 1 ) )
2014-11-25 04:03:13 +00:00
{
if ( v < 0 )
throw std : : underflow_error ( " negative width " ) ;
w + = v ;
2006-03-20 17:12:09 +00:00
}
return w ;
} else {
2014-12-02 03:35:01 +00:00
return font . ft_w * ( font . ft_maxchar - font . ft_minchar + 1 ) ;
2006-03-20 17:12:09 +00:00
}
}
2013-07-27 21:41:27 +00:00
2021-09-04 12:17:14 +00:00
static std : : pair < int , int > ogl_font_choose_size ( grs_font * font , const int gap )
{
2006-03-20 17:12:09 +00:00
int nchars = font - > ft_maxchar - font - > ft_minchar + 1 ;
int r , x , y , nc = 0 , smallest = 999999 , smallr = - 1 , tries ;
int smallprop = 10000 ;
2014-09-26 02:42:12 +00:00
int w ;
2021-09-04 12:17:14 +00:00
int rw = INT_MIN , rh = INT_MIN ;
2014-09-26 02:42:12 +00:00
for ( int h = 32 ; h < = 256 ; h * = 2 ) {
2006-03-20 17:12:09 +00:00
// h=pow2ize(font->ft_h*rows+gap*(rows-1));
if ( font - > ft_h > h ) continue ;
r = ( h / ( font - > ft_h + gap ) ) ;
2014-12-02 03:35:01 +00:00
w = pow2ize ( ( get_font_total_width ( * font ) + ( nchars - r ) * gap ) / r ) ;
2006-03-20 17:12:09 +00:00
tries = 0 ;
do {
if ( tries )
w = pow2ize ( w + 1 ) ;
if ( tries > 3 ) {
break ;
}
nc = 0 ;
y = 0 ;
while ( y + font - > ft_h < = h ) {
x = 0 ;
while ( x < w ) {
if ( nc = = nchars )
break ;
if ( font - > ft_flags & FT_PROPORTIONAL ) {
if ( x + font - > ft_widths [ nc ] + gap > w ) break ;
x + = font - > ft_widths [ nc + + ] + gap ;
} else {
if ( x + font - > ft_w + gap > w ) break ;
x + = font - > ft_w + gap ;
nc + + ;
}
}
if ( nc = = nchars )
break ;
y + = font - > ft_h + gap ;
}
tries + + ;
} while ( nc ! = nchars ) ;
if ( nc ! = nchars )
continue ;
if ( w * h = = smallest ) { //this gives squarer sizes priority (ie, 128x128 would be better than 512*32)
if ( w > = h ) {
if ( w / h < smallprop ) {
smallprop = w / h ;
smallest + + ; //hack
}
} else {
if ( h / w < smallprop ) {
smallprop = h / w ;
smallest + + ; //hack
}
}
}
if ( w * h < smallest ) {
smallr = 1 ;
smallest = w * h ;
2021-09-04 12:17:14 +00:00
rw = w ;
rh = h ;
2006-03-20 17:12:09 +00:00
}
}
if ( smallr < = 0 )
2016-11-13 05:27:18 +00:00
Error ( " Could not fit font? \n " ) ;
2021-09-04 12:17:14 +00:00
return { rw , rh } ;
2006-03-20 17:12:09 +00:00
}
2013-07-27 21:41:27 +00:00
static void ogl_init_font ( grs_font * font )
2008-10-29 11:03:31 +00:00
{
2010-09-02 13:55:28 +00:00
int oglflags = OGL_FLAG_ALPHA ;
2021-09-04 12:17:14 +00:00
const unsigned nchars = font - > ft_maxchar - font - > ft_minchar + 1 ;
2021-09-04 12:17:14 +00:00
int curx = 0 , cury = 0 ;
2008-10-29 11:03:31 +00:00
int gap = 1 ; // x/y offset between the chars so we can filter
2021-09-04 12:17:14 +00:00
const auto & & [ tw , th ] = ogl_font_choose_size ( font , gap ) ;
2019-02-02 18:36:39 +00:00
{
RAIIdmem < uint8_t [ ] > data ;
const unsigned length = tw * th ;
2021-06-28 03:37:48 +00:00
MALLOC ( data , uint8_t [ ] , length ) ;
2019-03-29 02:34:53 +00:00
std : : fill_n ( data . get ( ) , length , TRANSPARENCY_COLOR ) ; // map the whole data with transparency so we won't have borders if using gap
2019-02-02 18:36:39 +00:00
gr_init_main_bitmap ( font - > ft_parent_bitmap , bm_mode : : linear , 0 , 0 , tw , th , tw , std : : move ( data ) ) ;
}
2014-11-30 22:09:21 +00:00
gr_set_transparent ( font - > ft_parent_bitmap , 1 ) ;
2006-03-20 17:12:09 +00:00
if ( ! ( font - > ft_flags & FT_COLOR ) )
oglflags | = OGL_FLAG_NOCOLOR ;
2014-11-30 22:09:19 +00:00
ogl_init_texture ( * ( font - > ft_parent_bitmap . gltexture = ogl_get_free_texture ( ) ) , tw , th , oglflags ) ; // have to init the gltexture here so the subbitmaps will find it.
2006-03-20 17:12:09 +00:00
2020-05-02 21:18:42 +00:00
font - > ft_bitmaps = std : : make_unique < grs_bitmap [ ] > ( nchars ) ;
2021-09-04 12:17:14 +00:00
const unsigned h = font - > ft_h ;
2006-03-20 17:12:09 +00:00
2021-09-04 12:17:14 +00:00
for ( const auto i : xrange ( nchars ) )
2008-10-29 11:03:31 +00:00
{
2021-09-04 12:17:14 +00:00
const unsigned w = ( font - > ft_flags & FT_PROPORTIONAL )
? font - > ft_widths [ i ]
: font - > ft_w ;
2008-10-29 11:03:31 +00:00
if ( w < 1 | | w > 256 )
2008-04-06 20:23:28 +00:00
continue ;
2008-10-29 11:03:31 +00:00
if ( curx + w + gap > tw )
{
2006-03-20 17:12:09 +00:00
cury + = h + gap ;
curx = 0 ;
}
2008-10-29 11:03:31 +00:00
2006-03-20 17:12:09 +00:00
if ( cury + h > th )
Error ( " font doesn't really fit (%i/%i)? \n " , i , nchars ) ;
2008-10-29 11:03:31 +00:00
if ( font - > ft_flags & FT_COLOR )
{
2016-02-12 04:02:28 +00:00
const auto fp = ( font - > ft_flags & FT_PROPORTIONAL )
? font - > ft_chars [ i ]
: font - > ft_data + i * w * h ;
2021-09-04 12:17:14 +00:00
for ( const auto y : xrange ( h ) )
2008-10-29 11:03:31 +00:00
{
2021-09-04 12:17:14 +00:00
for ( const auto x : xrange ( w ) )
2014-12-02 03:24:38 +00:00
font - > ft_parent_bitmap . get_bitmap_data ( ) [ curx + x + ( cury + y ) * tw ] = fp [ x + y * w ] ;
2008-10-29 11:03:31 +00:00
// Let's call this a HACK:
// If we filter the fonts, the sliders will be messed up as the border pixels will have an
// alpha value while filtering. So the slider bitmaps will not look "connected".
// To prevent this, duplicate the first/last pixel-row with a 1-pixel offset.
2021-09-04 12:17:14 +00:00
if ( gap & & i > = 99 & & i < = 102 )
{
2008-10-29 11:03:31 +00:00
// See which bitmaps need left/right shifts:
// 99 = SLIDER_LEFT - shift RIGHT
// 100 = SLIDER_RIGHT - shift LEFT
// 101 = SLIDER_MIDDLE - shift LEFT+RIGHT
// 102 = SLIDER_MARKER - shift RIGHT
// shift left border
2021-09-04 12:17:14 +00:00
std : : size_t oi , ii ;
if ( i ! = 99 & & i ! = 102 )
{
oi = ( curx + ( cury + y ) * tw ) - 1 ;
ii = y * w ;
}
2008-10-29 11:03:31 +00:00
// shift right border
2021-09-04 12:17:14 +00:00
else if ( i ! = 100 )
{
oi = ( curx + ( w - 1 ) + ( cury + y ) * tw ) + 1 ;
ii = ( w - 1 ) + y * w ;
2008-10-29 11:03:31 +00:00
}
2021-09-04 12:17:14 +00:00
else
continue ;
font - > ft_parent_bitmap . get_bitmap_data ( ) [ oi ] = fp [ ii ] ;
2006-03-20 17:12:09 +00:00
}
2008-10-29 11:03:31 +00:00
}
}
else
{
2020-07-16 02:31:04 +00:00
auto white = gr_find_closest_color ( 63 , 63 , 63 ) ;
2016-02-12 04:02:28 +00:00
auto fp = ( font - > ft_flags & FT_PROPORTIONAL )
? font - > ft_chars [ i ]
: font - > ft_data + i * BITS_TO_BYTES ( w ) * h ;
2021-09-04 12:17:14 +00:00
for ( const auto y : xrange ( h ) )
{
uint8_t BitMask = 0 ;
uint8_t bits = 0 ;
for ( const auto x : xrange ( w ) )
2006-03-20 17:12:09 +00:00
{
if ( BitMask = = 0 ) {
bits = * fp + + ;
BitMask = 0x80 ;
}
2021-09-04 12:17:14 +00:00
font - > ft_parent_bitmap . get_bitmap_data ( ) [ curx + x + ( cury + y ) * tw ] = ( bits & BitMask )
? white
: 255 ;
2006-03-20 17:12:09 +00:00
BitMask > > = 1 ;
}
}
}
2014-11-30 22:09:21 +00:00
gr_init_sub_bitmap ( font - > ft_bitmaps [ i ] , font - > ft_parent_bitmap , curx , cury , w , h ) ;
2006-03-20 17:12:09 +00:00
curx + = w + gap ;
}
2016-09-17 13:56:43 +00:00
ogl_loadbmtexture_f ( font - > ft_parent_bitmap , CGameCfg . TexFilt , 0 , 0 ) ;
2006-03-20 17:12:09 +00:00
}
2021-08-26 03:13:45 +00:00
static void ogl_internal_string ( grs_canvas & canvas , const grs_font & cv_font , const int entry_x , int yy , const char * const s )
2006-03-20 17:12:09 +00:00
{
2021-08-26 03:13:45 +00:00
auto orig_color = canvas . cv_font_fg_color ; //to allow easy reseting to default string color with colored strings -MPM
2006-03-20 17:12:09 +00:00
2016-05-28 17:31:26 +00:00
if ( grd_curscreen - > sc_canvas . cv_bitmap . get_type ( ) ! = bm_mode : : ogl )
2006-03-20 17:12:09 +00:00
Error ( " carp. \n " ) ;
2016-02-10 03:58:05 +00:00
const auto & & fspacy1 = FSPACY ( 1 ) ;
2016-02-12 04:02:28 +00:00
const auto & & INFONT = font_character_extent ( cv_font ) ;
2016-02-10 03:58:05 +00:00
const auto & & fontscale_x = FONTSCALE_X ( ) ;
const auto & & FONTSCALE_Y_ft_h = FONTSCALE_Y ( cv_font . ft_h ) ;
2016-02-12 04:02:28 +00:00
ogl_colors colors ;
2021-08-26 03:13:45 +00:00
for ( auto next_row = s ; next_row ; )
2006-03-20 17:12:09 +00:00
{
2021-08-26 03:13:45 +00:00
auto text_ptr = std : : exchange ( next_row , nullptr ) ;
auto line_x = entry_x = = 0x8000
? get_centered_x ( canvas , cv_font , text_ptr )
: entry_x ;
2006-03-20 17:12:09 +00:00
2021-08-26 03:13:45 +00:00
for ( ; const auto c0 = * text_ptr ; )
2006-03-20 17:12:09 +00:00
{
2021-08-26 03:13:45 +00:00
if ( c0 = = ' \n ' )
2006-03-20 17:12:09 +00:00
{
next_row = & text_ptr [ 1 ] ;
2016-02-10 03:58:05 +00:00
yy + = FONTSCALE_Y_ft_h + fspacy1 ;
2006-03-20 17:12:09 +00:00
break ;
}
2021-08-26 03:13:45 +00:00
const auto letter = c0 - cv_font . ft_minchar ;
2006-03-20 17:12:09 +00:00
2021-08-26 03:13:45 +00:00
const auto & result = get_char_width < int > ( cv_font , c0 , text_ptr [ 1 ] ) ;
2015-05-09 17:39:01 +00:00
const auto & spacing = result . spacing ;
2006-03-20 17:12:09 +00:00
2021-08-26 03:13:45 +00:00
uint8_t underline = 0 ;
if ( ! INFONT ( letter ) | | c0 < = 0x06 ) //not in font, draw as space
2010-07-01 09:03:52 +00:00
{
2006-10-09 21:44:24 +00:00
CHECK_EMBEDDED_COLORS ( ) else {
2021-08-26 03:13:45 +00:00
line_x + = spacing ;
2006-10-09 21:44:24 +00:00
text_ptr + + ;
}
2012-04-09 09:30:40 +00:00
if ( underline )
{
2020-07-16 02:31:04 +00:00
const auto color = canvas . cv_font_fg_color ;
2021-08-26 03:13:45 +00:00
gr_rect ( canvas , line_x , yy + cv_font . ft_baseline + 2 , line_x + cv_font . ft_w , yy + cv_font . ft_baseline + 3 , color ) ;
2012-04-09 09:30:40 +00:00
}
2006-03-20 17:12:09 +00:00
continue ;
}
2016-02-10 03:58:05 +00:00
const auto ft_w = ( cv_font . ft_flags & FT_PROPORTIONAL )
? cv_font . ft_widths [ letter ]
: cv_font . ft_w ;
2012-03-30 12:36:35 +00:00
2021-11-01 03:37:18 +00:00
ogl_ubitmapm_cs ( canvas , line_x , yy , fontscale_x ( ft_w ) , FONTSCALE_Y_ft_h , cv_font . ft_bitmaps [ letter ] , ( cv_font . ft_flags & FT_COLOR ) ? colors . white : ( canvas . cv_bitmap . get_type ( ) = = bm_mode : : ogl ) ? colors . init ( canvas . cv_font_fg_color ) : throw std : : runtime_error ( " non-color string to non-ogl dest " ) ) ;
2006-03-20 17:12:09 +00:00
2021-08-26 03:13:45 +00:00
line_x + = spacing ;
2006-03-20 17:12:09 +00:00
text_ptr + + ;
}
}
}
2018-05-19 23:21:42 +00:00
# define gr_internal_color_string ogl_internal_string
2006-03-20 17:12:09 +00:00
# endif //OGL
2020-12-26 21:17:29 +00:00
}
2018-05-19 23:21:42 +00:00
void gr_string ( grs_canvas & canvas , const grs_font & cv_font , const int x , const int y , const char * const s )
2015-09-29 02:41:22 +00:00
{
2021-09-12 16:20:52 +00:00
const auto & & [ w , h ] = gr_get_string_size ( cv_font , s ) ;
2018-05-19 23:21:42 +00:00
gr_string ( canvas , cv_font , x , y , s , w , h ) ;
2015-09-29 02:41:22 +00:00
}
2020-12-26 21:17:29 +00:00
namespace {
2018-05-19 23:21:42 +00:00
static void gr_ustring_mono ( grs_canvas & canvas , const grs_font & cv_font , const int x , const int y , const char * const s )
2006-03-20 17:12:09 +00:00
{
2017-01-08 22:31:59 +00:00
switch ( canvas . cv_bitmap . get_type ( ) )
2016-01-10 21:18:09 +00:00
{
2016-05-28 17:31:26 +00:00
case bm_mode : : linear :
2021-09-04 12:17:14 +00:00
if ( canvas . cv_font_bg_color = = TRANSPARENCY_COLOR )
2018-05-19 23:21:42 +00:00
gr_internal_string0m ( canvas , cv_font , x , y , s ) ;
2016-01-10 21:18:09 +00:00
else
2018-05-19 23:21:42 +00:00
gr_internal_string0 ( canvas , cv_font , x , y , s ) ;
2021-09-12 16:20:52 +00:00
break ;
case bm_mode : : ilbm :
case bm_mode : : rgb15 :
# if DXX_USE_OGL
case bm_mode : : ogl :
# endif
break ;
2016-01-10 21:18:09 +00:00
}
}
2006-03-20 17:12:09 +00:00
2020-12-26 21:17:29 +00:00
}
2018-05-19 23:21:42 +00:00
void gr_string ( grs_canvas & canvas , const grs_font & cv_font , const int x , const int y , const char * const s , const int w , const int h )
2016-01-10 21:18:09 +00:00
{
2016-02-06 22:12:55 +00:00
if ( y + h < 0 )
return ;
2017-01-08 22:31:59 +00:00
const auto bm_h = canvas . cv_bitmap . bm_h ;
2015-09-29 02:41:22 +00:00
if ( y > bm_h )
return ;
2017-01-08 22:31:59 +00:00
const auto bm_w = canvas . cv_bitmap . bm_w ;
2016-01-10 21:18:09 +00:00
int xw = w ;
2006-03-20 17:12:09 +00:00
if ( x = = 0x8000 ) {
// for x, since this will be centered, only look at
// width.
} else {
2015-09-29 02:41:22 +00:00
if ( x > bm_w )
return ;
2016-01-10 21:18:09 +00:00
xw + = x ;
2016-02-06 22:12:55 +00:00
if ( xw < 0 )
return ;
2013-12-01 19:13:39 +00:00
}
2016-01-10 21:18:09 +00:00
if (
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2017-01-08 22:31:59 +00:00
canvas . cv_bitmap . get_type ( ) = = bm_mode : : ogl | |
2016-01-10 21:18:09 +00:00
# endif
2018-05-19 23:21:42 +00:00
cv_font . ft_flags & FT_COLOR )
2013-12-01 19:13:39 +00:00
{
2018-05-19 23:21:42 +00:00
gr_internal_color_string ( canvas , cv_font , x , y , s ) ;
2013-12-01 19:13:39 +00:00
return ;
}
2016-01-10 21:18:09 +00:00
// Partially clipped...
if ( ! ( y < 0 | |
x < 0 | |
xw > bm_w | |
y + h > bm_h ) )
2013-12-01 19:13:39 +00:00
{
2018-05-19 23:21:42 +00:00
gr_ustring_mono ( canvas , cv_font , x , y , s ) ;
2013-12-01 19:13:39 +00:00
return ;
}
2006-03-20 17:12:09 +00:00
2021-09-04 12:17:14 +00:00
if ( canvas . cv_font_bg_color = = TRANSPARENCY_COLOR )
2013-12-01 19:13:39 +00:00
{
2018-05-19 23:21:42 +00:00
gr_internal_string_clipped_m ( canvas , cv_font , x , y , s ) ;
2013-12-01 19:13:39 +00:00
return ;
}
2018-05-19 23:21:42 +00:00
gr_internal_string_clipped ( canvas , cv_font , x , y , s ) ;
2006-03-20 17:12:09 +00:00
}
2018-05-19 23:21:42 +00:00
void gr_ustring ( grs_canvas & canvas , const grs_font & cv_font , const int x , const int y , const char * const s )
2006-03-20 17:12:09 +00:00
{
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2017-01-08 22:32:00 +00:00
if ( canvas . cv_bitmap . get_type ( ) = = bm_mode : : ogl )
2013-12-01 19:13:39 +00:00
{
2018-05-19 23:21:42 +00:00
ogl_internal_string ( canvas , cv_font , x , y , s ) ;
2013-12-01 19:13:39 +00:00
return ;
}
2006-03-20 17:12:09 +00:00
# endif
2020-12-27 22:03:09 +00:00
if ( cv_font . ft_flags & FT_COLOR )
2018-05-19 23:21:42 +00:00
gr_internal_color_string ( canvas , cv_font , x , y , s ) ;
2006-03-20 17:12:09 +00:00
else
2018-05-19 23:21:42 +00:00
gr_ustring_mono ( canvas , cv_font , x , y , s ) ;
2006-03-20 17:12:09 +00:00
}
2021-09-12 16:20:52 +00:00
unsigned gr_get_string_height ( const grs_font & cv_font , const unsigned lines )
Fix mouse-selection in listboxes
Commit d355ef4030ef removed a seemingly unnecessary modification of the
global variable grd_curcanv->cv_font, after eliminating all local reads
of it. However, a non-local read, buried in listbox_mouse, depended on
grd_curcanv->cv_font being set to a MEDIUM font. After that commit,
grd_curcanv->cv_font retained its prior value, which is not a MEDIUM
font. This caused listbox_mouse to compute an incorrect height of the
lines in the listbox, which manifested as the game choosing the wrong
line when the mouse is clicked in the listbox.
Fix the problem by explicitly using MEDIUM3_FONT, since that was
usually the value left in grd_curcanv->cv_font prior to that commit. In
some cases, a different MEDIUM font would be left there, but all the
MEDIUM fonts have the same height, so they are interchangeable for this
purpose.
Reported-by: Q3BFG10K <https://github.com/dxx-rebirth/dxx-rebirth/issues/498>
Fixes: d355ef4030ef4c8e9e3a153fa2c04e56192edc26 ("Pass font to various drawing functions")
2020-04-04 19:30:23 +00:00
{
const auto fontscale_y = FONTSCALE_Y ( cv_font . ft_h ) ;
2021-09-12 16:20:52 +00:00
return static_cast < unsigned > ( fontscale_y + ( lines * ( fontscale_y + FSPACY ( 1 ) ) ) ) ;
Fix mouse-selection in listboxes
Commit d355ef4030ef removed a seemingly unnecessary modification of the
global variable grd_curcanv->cv_font, after eliminating all local reads
of it. However, a non-local read, buried in listbox_mouse, depended on
grd_curcanv->cv_font being set to a MEDIUM font. After that commit,
grd_curcanv->cv_font retained its prior value, which is not a MEDIUM
font. This caused listbox_mouse to compute an incorrect height of the
lines in the listbox, which manifested as the game choosing the wrong
line when the mouse is clicked in the listbox.
Fix the problem by explicitly using MEDIUM3_FONT, since that was
usually the value left in grd_curcanv->cv_font prior to that commit. In
some cases, a different MEDIUM font would be left there, but all the
MEDIUM fonts have the same height, so they are interchangeable for this
purpose.
Reported-by: Q3BFG10K <https://github.com/dxx-rebirth/dxx-rebirth/issues/498>
Fixes: d355ef4030ef4c8e9e3a153fa2c04e56192edc26 ("Pass font to various drawing functions")
2020-04-04 19:30:23 +00:00
}
2021-09-12 16:20:52 +00:00
gr_string_size gr_get_string_size ( const grs_font & cv_font , const char * s )
2018-06-27 03:34:34 +00:00
{
2021-09-12 16:20:52 +00:00
return gr_get_string_size ( cv_font , s , UINT_MAX ) ;
2018-06-27 03:34:34 +00:00
}
2021-09-12 16:20:52 +00:00
gr_string_size gr_get_string_size ( const grs_font & cv_font , const char * s , const unsigned max_chars_per_line )
2006-03-20 17:12:09 +00:00
{
2015-06-13 22:42:20 +00:00
float longest_width = 0.0 , string_width_f = 0.0 ;
unsigned lines = 0 ;
2015-09-29 02:41:22 +00:00
if ( s )
2006-03-20 17:12:09 +00:00
{
2018-06-27 03:34:34 +00:00
unsigned remaining_chars_this_line = max_chars_per_line ;
2006-03-20 17:12:09 +00:00
while ( * s )
{
2015-06-13 22:42:20 +00:00
if ( * s = = ' \n ' )
2006-03-20 17:12:09 +00:00
{
2015-06-13 22:42:20 +00:00
if ( longest_width < string_width_f )
longest_width = string_width_f ;
2010-07-01 09:03:52 +00:00
string_width_f = 0 ;
2015-06-13 22:42:20 +00:00
const auto os = s ;
while ( * + + s = = ' \n ' )
{
}
lines + = s - os ;
if ( ! * s )
break ;
2018-06-27 03:34:34 +00:00
remaining_chars_this_line = max_chars_per_line ;
2006-03-20 17:12:09 +00:00
}
2015-12-22 04:18:50 +00:00
const auto & result = get_char_width < float > ( cv_font , s [ 0 ] , s [ 1 ] ) ;
2015-05-09 17:39:01 +00:00
const auto & spacing = result . spacing ;
2010-07-01 09:03:52 +00:00
string_width_f + = spacing ;
s + + ;
2018-06-27 03:34:34 +00:00
if ( ! - - remaining_chars_this_line )
break ;
2006-03-20 17:12:09 +00:00
}
}
2021-09-12 16:20:52 +00:00
return { static_cast < unsigned long > ( std : : max ( longest_width , string_width_f ) ) , gr_get_string_height ( cv_font , lines ) } ;
2006-03-20 17:12:09 +00:00
}
2017-02-11 21:42:44 +00:00
std : : pair < const char * , unsigned > gr_get_string_wrap ( const grs_font & cv_font , const char * s , unsigned limit )
2015-12-22 04:18:52 +00:00
{
assert ( s ) ;
float string_width_f = 0.0 ;
const float limit_f = limit ;
for ( uint8_t c ; ( c = * s ) ; + + s )
{
const auto & & spacing = get_char_width < float > ( cv_font , c , s [ 1 ] ) . spacing ;
const float next_string_width = string_width_f + spacing ;
if ( next_string_width > = limit_f )
break ;
string_width_f = next_string_width ;
}
return { s , static_cast < unsigned > ( string_width_f ) } ;
}
2006-03-20 17:12:09 +00:00
2018-05-19 23:21:42 +00:00
template < void ( & F ) ( grs_canvas & , const grs_font & , int , int , const char * ) >
void ( gr_printt ) ( grs_canvas & canvas , const grs_font & cv_font , const int x , const int y , const char * const format , . . . )
2006-03-20 17:12:09 +00:00
{
char buffer [ 1000 ] ;
va_list args ;
va_start ( args , format ) ;
2016-01-10 21:18:08 +00:00
vsnprintf ( buffer , sizeof ( buffer ) , format , args ) ;
2013-12-01 18:27:54 +00:00
va_end ( args ) ;
2018-05-19 23:21:42 +00:00
F ( canvas , cv_font , x , y , buffer ) ;
2006-03-20 17:12:09 +00:00
}
2018-05-19 23:21:42 +00:00
template void gr_printt < gr_string > ( grs_canvas & , const grs_font & , int , int , const char * , . . . ) ;
template void gr_printt < gr_ustring > ( grs_canvas & , const grs_font & , int , int , const char * , . . . ) ;
2006-03-20 17:12:09 +00:00
2014-07-20 03:48:27 +00:00
void gr_close_font ( std : : unique_ptr < grs_font > font )
2006-03-20 17:12:09 +00:00
{
if ( font )
{
//find font in list
2016-01-10 21:18:09 +00:00
if ( ! ( font - > ft_flags & FT_COLOR ) )
return ;
2014-07-20 03:48:27 +00:00
auto e = end ( open_font ) ;
2016-01-10 21:18:09 +00:00
auto i = std : : find ( begin ( open_font ) , e , font . get ( ) ) ;
2014-07-20 03:48:27 +00:00
if ( i = = e )
throw std : : logic_error ( " closing non-open font " ) ;
2016-01-10 21:18:09 +00:00
* i = nullptr ;
2006-03-20 17:12:09 +00:00
}
}
2013-06-08 22:24:17 +00:00
//remap a font, re-reading its data & palette
2016-02-06 22:12:55 +00:00
static void gr_remap_font ( grs_font * font , const char * fontname ) ;
2013-06-08 22:24:17 +00:00
2006-03-20 17:12:09 +00:00
//remap (by re-reading) all the color fonts
void gr_remap_color_fonts ( )
{
2016-01-10 21:18:09 +00:00
range_for ( auto font , open_font )
2014-11-25 04:03:13 +00:00
{
2016-01-10 21:18:09 +00:00
if ( font )
2016-02-06 22:12:55 +00:00
gr_remap_font ( font , & font - > ft_filename [ 0 ] ) ;
2006-03-20 17:12:09 +00:00
}
}
2015-12-13 18:00:48 +00:00
2020-12-26 21:17:29 +00:00
namespace {
2006-03-20 17:12:09 +00:00
/*
2016-01-09 16:38:14 +00:00
* reads a grs_font structure from a PHYSFS_File
2006-03-20 17:12:09 +00:00
*/
2016-01-09 16:38:14 +00:00
static void grs_font_read ( grs_font * gf , PHYSFS_File * fp )
2006-03-20 17:12:09 +00:00
{
2011-06-01 07:59:55 +00:00
gf - > ft_w = PHYSFSX_readShort ( fp ) ;
gf - > ft_h = PHYSFSX_readShort ( fp ) ;
gf - > ft_flags = PHYSFSX_readShort ( fp ) ;
gf - > ft_baseline = PHYSFSX_readShort ( fp ) ;
gf - > ft_minchar = PHYSFSX_readByte ( fp ) ;
gf - > ft_maxchar = PHYSFSX_readByte ( fp ) ;
2015-11-14 18:17:21 +00:00
PHYSFSX_readShort ( fp ) ;
2015-11-14 18:17:21 +00:00
gf - > ft_data = reinterpret_cast < uint8_t * > ( static_cast < uintptr_t > ( PHYSFSX_readInt ( fp ) ) - GRS_FONT_SIZE ) ;
2014-08-07 03:05:48 +00:00
PHYSFSX_readInt ( fp ) ;
2015-11-14 18:17:21 +00:00
gf - > ft_widths = reinterpret_cast < int16_t * > ( static_cast < uintptr_t > ( PHYSFSX_readInt ( fp ) ) - GRS_FONT_SIZE ) ;
gf - > ft_kerndata = reinterpret_cast < uint8_t * > ( static_cast < uintptr_t > ( PHYSFSX_readInt ( fp ) ) - GRS_FONT_SIZE ) ;
2006-03-20 17:12:09 +00:00
}
2016-02-06 22:12:55 +00:00
static std : : unique_ptr < grs_font > gr_internal_init_font ( const char * fontname )
2006-03-20 17:12:09 +00:00
{
2016-02-12 04:02:28 +00:00
const uint8_t * ptr ;
2020-07-16 02:31:04 +00:00
color_palette_index * ft_data ;
2015-11-14 18:17:21 +00:00
struct {
2020-05-02 21:18:42 +00:00
std : : array < char , 4 > magic ;
2015-11-14 18:17:21 +00:00
unsigned datasize ; //size up to (but not including) palette
} file_header ;
2006-03-20 17:12:09 +00:00
2021-07-25 23:00:56 +00:00
auto & & [ fontfile , physfserr ] = PHYSFSX_openReadBuffered ( fontname ) ;
2006-03-20 17:12:09 +00:00
if ( ! fontfile ) {
2021-07-25 23:00:56 +00:00
con_printf ( CON_VERBOSE , " Failed to open font file \" %s \" : %s " , fontname , PHYSFS_getErrorByCode ( physfserr ) ) ;
2014-07-20 03:48:27 +00:00
return { } ;
2006-03-20 17:12:09 +00:00
}
2015-11-14 18:17:21 +00:00
static_assert ( sizeof ( file_header ) = = 8 , " file header size error " ) ;
if ( PHYSFS_read ( fontfile , & file_header , sizeof ( file_header ) , 1 ) ! = 1 | |
memcmp ( file_header . magic . data ( ) , " PSFN " , 4 ) | |
2015-12-04 03:36:32 +00:00
( file_header . datasize = INTEL_INT ( file_header . datasize ) ) < GRS_FONT_SIZE )
2015-11-14 18:17:21 +00:00
{
con_printf ( CON_NORMAL , " Invalid header in font file %s " , fontname ) ;
2014-07-20 03:48:27 +00:00
return { } ;
2006-03-20 17:12:09 +00:00
}
2015-12-04 03:36:32 +00:00
file_header . datasize - = GRS_FONT_SIZE ; // subtract the size of the header.
const auto & datasize = file_header . datasize ;
2006-03-20 17:12:09 +00:00
2020-05-02 21:18:42 +00:00
auto font = std : : make_unique < grs_font > ( ) ;
2014-07-20 03:48:27 +00:00
grs_font_read ( font . get ( ) , fontfile ) ;
2006-03-20 17:12:09 +00:00
2016-02-12 04:02:28 +00:00
const unsigned nchars = font - > ft_maxchar - font - > ft_minchar + 1 ;
std : : size_t ft_chars_storage = ( font - > ft_flags & FT_PROPORTIONAL )
? sizeof ( uint8_t * ) * nchars
: 0 ;
2020-07-16 02:31:04 +00:00
auto ft_allocdata = std : : make_unique < color_palette_index [ ] > ( datasize + ft_chars_storage ) ;
2016-02-12 04:02:28 +00:00
const auto font_data = & ft_allocdata [ ft_chars_storage ] ;
2015-11-14 18:17:21 +00:00
if ( PHYSFS_read ( fontfile , font_data , 1 , datasize ) ! = datasize )
{
con_printf ( CON_URGENT , " Insufficient data in font file %s " , fontname ) ;
return { } ;
}
2006-03-20 17:12:09 +00:00
if ( font - > ft_flags & FT_PROPORTIONAL ) {
2015-11-14 18:17:21 +00:00
const auto offset_widths = reinterpret_cast < uintptr_t > ( font - > ft_widths ) ;
auto w = reinterpret_cast < short * > ( & font_data [ offset_widths ] ) ;
if ( offset_widths > = datasize | | offset_widths + ( nchars * sizeof ( * w ) ) > = datasize )
{
con_printf ( CON_URGENT , " Missing widths in font file %s " , fontname ) ;
return { } ;
}
font - > ft_widths = w ;
const auto offset_data = reinterpret_cast < uintptr_t > ( font - > ft_data ) ;
if ( offset_data > = datasize )
{
con_printf ( CON_URGENT , " Missing data in font file %s " , fontname ) ;
return { } ;
}
2016-07-15 03:43:03 +00:00
font - > ft_data = ptr = ft_data = & font_data [ offset_data ] ;
2016-02-12 04:02:28 +00:00
const auto ft_chars = reinterpret_cast < const uint8_t * * > ( ft_allocdata . get ( ) ) ;
font - > ft_chars = reinterpret_cast < const uint8_t * const * > ( ft_chars ) ;
2006-03-20 17:12:09 +00:00
2015-10-09 02:46:09 +00:00
const unsigned is_color = font - > ft_flags & FT_COLOR ;
const unsigned ft_h = font - > ft_h ;
2016-02-12 04:02:28 +00:00
std : : generate_n ( ft_chars , nchars , [ is_color , ft_h , & w , & ptr ] {
2015-10-09 02:46:09 +00:00
const unsigned s = INTEL_SHORT ( * w ) ;
2020-07-05 23:34:33 +00:00
if constexpr ( words_bigendian )
2015-10-09 02:46:09 +00:00
* w = static_cast < uint16_t > ( s ) ;
+ + w ;
const auto r = ptr ;
ptr + = ft_h * ( is_color ? s : BITS_TO_BYTES ( s ) ) ;
return r ;
} ) ;
2006-03-20 17:12:09 +00:00
} else {
2016-02-12 04:02:28 +00:00
font - > ft_data = ft_data = font_data ;
2006-03-20 17:12:09 +00:00
font - > ft_widths = NULL ;
ptr = font - > ft_data + ( nchars * font - > ft_w * font - > ft_h ) ;
}
if ( font - > ft_flags & FT_KERNED )
2015-11-14 18:17:21 +00:00
{
const auto offset_kerndata = reinterpret_cast < uintptr_t > ( font - > ft_kerndata ) ;
if ( datasize < = offset_kerndata )
{
con_printf ( CON_URGENT , " Missing kerndata in font file %s " , fontname ) ;
return { } ;
}
const auto begin_kerndata = reinterpret_cast < const unsigned char * > ( & font_data [ offset_kerndata ] ) ;
const auto end_font_data = & font_data [ datasize - ( ( datasize - offset_kerndata + 2 ) % 3 ) ] ;
for ( auto cur_kerndata = begin_kerndata ; ; cur_kerndata + = 3 )
{
if ( cur_kerndata = = end_font_data )
{
con_printf ( CON_URGENT , " Unterminated kerndata in font file %s " , fontname ) ;
return { } ;
}
if ( * cur_kerndata = = kerndata_terminator )
break ;
}
font - > ft_kerndata = begin_kerndata ;
}
else
font - > ft_kerndata = nullptr ;
2006-03-20 17:12:09 +00:00
if ( font - > ft_flags & FT_COLOR ) { //remap palette
2013-01-06 21:03:57 +00:00
palette_array_t palette ;
2020-07-16 02:31:04 +00:00
std : : array < color_palette_index , 256 > colormap ;
2018-03-08 04:21:18 +00:00
/* `freq` exists so that decode_data can write to it, but it is
* otherwise unused . ` decode_data ` is not guaranteed to write
2020-07-16 02:31:04 +00:00
* to every element , but the bitset constructor will initialize
* the storage , so reading unwritten fields will always return
* false .
2018-03-08 04:21:18 +00:00
*/
2020-07-16 02:31:04 +00:00
std : : bitset < 256 > freq ;
2006-03-20 17:12:09 +00:00
2013-01-06 21:11:53 +00:00
PHYSFS_read ( fontfile , & palette [ 0 ] , sizeof ( palette [ 0 ] ) , palette . size ( ) ) ; //read the palette
2006-03-20 17:12:09 +00:00
2018-03-08 04:21:18 +00:00
build_colormap_good ( palette , colormap ) ;
2006-03-20 17:12:09 +00:00
colormap [ TRANSPARENCY_COLOR ] = TRANSPARENCY_COLOR ; // changed from colormap[255] = 255 to this for macintosh
2016-02-12 04:02:28 +00:00
decode_data ( ft_data , ptr - font - > ft_data , colormap , freq ) ;
2006-03-20 17:12:09 +00:00
}
2015-01-17 18:31:42 +00:00
fontfile . reset ( ) ;
2006-03-20 17:12:09 +00:00
//set curcanv vars
2016-01-10 21:18:09 +00:00
auto & ft_filename = font - > ft_filename ;
2016-02-12 04:02:28 +00:00
font - > ft_allocdata = move ( ft_allocdata ) ;
2016-01-10 21:18:09 +00:00
strncpy ( & ft_filename [ 0 ] , fontname , ft_filename . size ( ) ) ;
2016-02-06 22:12:55 +00:00
return font ;
}
2020-12-26 21:17:29 +00:00
}
2017-03-11 19:56:22 +00:00
grs_font_ptr gr_init_font ( grs_canvas & canvas , const char * fontname )
2016-02-06 22:12:55 +00:00
{
auto font = gr_internal_init_font ( fontname ) ;
if ( ! font )
return { } ;
2017-03-11 19:56:22 +00:00
canvas . cv_font = font . get ( ) ;
canvas . cv_font_fg_color = 0 ;
canvas . cv_font_bg_color = 0 ;
2016-01-10 21:18:09 +00:00
if ( font - > ft_flags & FT_COLOR )
{
auto e = end ( open_font ) ;
auto i = std : : find ( begin ( open_font ) , e , static_cast < grs_font * > ( nullptr ) ) ;
if ( i = = e )
throw std : : logic_error ( " too many open fonts " ) ;
* i = font . get ( ) ;
}
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2016-02-06 22:12:55 +00:00
ogl_init_font ( font . get ( ) ) ;
# endif
2014-07-20 03:48:27 +00:00
return grs_font_ptr ( font . release ( ) ) ;
2006-03-20 17:12:09 +00:00
}
//remap a font by re-reading its data & palette
2016-02-06 22:12:55 +00:00
void gr_remap_font ( grs_font * font , const char * fontname )
2006-03-20 17:12:09 +00:00
{
2016-02-06 22:12:55 +00:00
auto n = gr_internal_init_font ( fontname ) ;
if ( ! n )
return ;
if ( ! ( n - > ft_flags & FT_COLOR ) )
return ;
* font = std : : move ( * n . get ( ) ) ;
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2006-03-20 17:12:09 +00:00
ogl_init_font ( font ) ;
# endif
2015-12-13 18:00:48 +00:00
}
2020-12-27 22:03:09 +00:00
void gr_set_curfont ( grs_canvas & canvas , const grs_font & n )
2006-03-20 17:12:09 +00:00
{
2020-12-27 22:03:09 +00:00
canvas . cv_font = & n ;
2006-03-20 17:12:09 +00:00
}
2020-12-26 21:17:29 +00:00
namespace {
2015-05-09 17:38:59 +00:00
template < bool masked_draws_background >
2018-05-19 23:21:42 +00:00
static int gr_internal_string_clipped_template ( grs_canvas & canvas , const grs_font & cv_font , int x , int y , const char * const s )
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:39:01 +00:00
int letter ;
2006-03-20 17:12:09 +00:00
int x1 = x , last_x ;
2016-02-12 04:02:28 +00:00
const auto & & INFONT = font_character_extent ( cv_font ) ;
2015-05-09 17:39:00 +00:00
const auto ft_flags = cv_font . ft_flags ;
const auto proportional = ft_flags & FT_PROPORTIONAL ;
2017-01-08 22:31:59 +00:00
const auto cv_font_bg_color = canvas . cv_font_bg_color ;
2015-05-09 17:38:59 +00:00
2020-07-16 02:31:04 +00:00
for ( auto next_row = s ; next_row ; )
2006-03-20 17:12:09 +00:00
{
2020-07-16 02:31:04 +00:00
const auto text_ptr1 = next_row ;
2006-03-20 17:12:09 +00:00
next_row = NULL ;
x = x1 ;
if ( x = = 0x8000 ) //centered
2017-03-11 19:56:23 +00:00
x = get_centered_x ( canvas , cv_font , text_ptr1 ) ;
2006-03-20 17:12:09 +00:00
last_x = x ;
2021-09-04 12:17:14 +00:00
for ( const auto r : xrange ( cv_font . ft_h ) )
{
2020-07-16 02:31:04 +00:00
auto text_ptr = text_ptr1 ;
2006-03-20 17:12:09 +00:00
x = last_x ;
2015-05-09 17:39:00 +00:00
for ( ; const uint8_t c0 = * text_ptr ; + + text_ptr )
{
if ( c0 = = ' \n ' )
{
2006-03-20 17:12:09 +00:00
next_row = & text_ptr [ 1 ] ;
break ;
}
2015-05-09 17:39:00 +00:00
if ( c0 = = CC_COLOR )
{
2017-01-08 22:31:59 +00:00
canvas . cv_font_fg_color = static_cast < uint8_t > ( * + + text_ptr ) ;
2006-03-20 17:12:09 +00:00
continue ;
}
2015-05-09 17:39:00 +00:00
if ( c0 = = CC_LSPACING )
{
2006-03-20 17:12:09 +00:00
Int3 ( ) ; // Warning: skip lines not supported for clipped strings.
2015-05-09 17:39:00 +00:00
text_ptr + = 1 ;
2006-03-20 17:12:09 +00:00
continue ;
}
2015-05-09 17:39:00 +00:00
const auto underline = unlikely ( c0 = = CC_UNDERLINE )
2015-05-09 17:38:59 +00:00
? + + text_ptr , r = = cv_font . ft_baseline + 2 | | r = = cv_font . ft_baseline + 3
: 0 ;
2015-05-09 17:39:00 +00:00
const uint8_t c = * text_ptr ;
2015-12-22 04:18:50 +00:00
const auto & result = get_char_width < int > ( cv_font , c , text_ptr [ 1 ] ) ;
2015-05-09 17:39:01 +00:00
const auto & width = result . width ;
const auto & spacing = result . spacing ;
2006-03-20 17:12:09 +00:00
2015-05-09 17:39:00 +00:00
letter = c - cv_font . ft_minchar ;
2006-03-20 17:12:09 +00:00
if ( ! INFONT ( letter ) ) { //not in font, draw as space
x + = spacing ;
continue ;
}
2017-01-08 22:31:59 +00:00
const auto cv_font_fg_color = canvas . cv_font_fg_color ;
2016-02-12 04:02:28 +00:00
auto color = cv_font_fg_color ;
2015-09-13 21:02:19 +00:00
if ( width )
{
2006-03-20 17:12:09 +00:00
if ( underline ) {
2015-05-09 17:38:59 +00:00
for ( uint_fast32_t i = width ; i - - ; )
{
2017-03-11 19:56:23 +00:00
gr_pixel ( canvas . cv_bitmap , x + + , y , color ) ;
2006-03-20 17:12:09 +00:00
}
} else {
2015-05-09 17:39:00 +00:00
auto fp = proportional ? cv_font . ft_chars [ letter ] : cv_font . ft_data + letter * BITS_TO_BYTES ( width ) * cv_font . ft_h ;
2006-03-20 17:12:09 +00:00
fp + = BITS_TO_BYTES ( width ) * r ;
2015-09-13 21:02:19 +00:00
/* Setting bits=0 is a dead store, but is necessary to
* prevent - Og - Wuninitialized from issuing a bogus
* warning . - Og does not see that bits_remaining = 0
* guarantees that bits will be initialized from * fp before
* it is read .
*/
uint8_t bits_remaining = 0 , bits = 0 ;
2006-03-20 17:12:09 +00:00
2015-09-13 21:02:19 +00:00
for ( uint_fast32_t i = width ; i - - ; + + x , - - bits_remaining )
2015-05-09 17:38:59 +00:00
{
2015-09-13 21:02:19 +00:00
if ( ! bits_remaining )
{
2006-03-20 17:12:09 +00:00
bits = * fp + + ;
2015-09-13 21:02:19 +00:00
bits_remaining = 8 ;
2006-03-20 17:12:09 +00:00
}
2015-09-24 03:05:16 +00:00
const auto bit_enabled = ( bits & 0x80 ) ;
bits < < = 1 ;
2020-07-16 02:31:04 +00:00
if constexpr ( masked_draws_background )
2016-02-12 04:02:28 +00:00
color = bit_enabled ? cv_font_fg_color : cv_font_bg_color ;
2006-03-20 17:12:09 +00:00
else
2015-05-09 17:38:59 +00:00
{
2015-05-09 17:39:00 +00:00
if ( ! bit_enabled )
2015-05-09 17:38:59 +00:00
continue ;
}
2017-03-11 19:56:23 +00:00
gr_pixel ( canvas . cv_bitmap , x , y , color ) ;
2006-03-20 17:12:09 +00:00
}
}
2015-09-13 21:02:19 +00:00
}
2006-03-20 17:12:09 +00:00
x + = spacing - width ; //for kerning
}
y + + ;
}
}
return 0 ;
}
2018-05-19 23:21:42 +00:00
static int gr_internal_string_clipped_m ( grs_canvas & canvas , const grs_font & cv_font , const int x , const int y , const char * const s )
2006-03-20 17:12:09 +00:00
{
2018-05-19 23:21:42 +00:00
return gr_internal_string_clipped_template < true > ( canvas , cv_font , x , y , s ) ;
2015-05-09 17:38:59 +00:00
}
2006-03-20 17:12:09 +00:00
2018-05-19 23:21:42 +00:00
static int gr_internal_string_clipped ( grs_canvas & canvas , const grs_font & cv_font , const int x , const int y , const char * const s )
2015-05-09 17:38:59 +00:00
{
2018-05-19 23:21:42 +00:00
return gr_internal_string_clipped_template < false > ( canvas , cv_font , x , y , s ) ;
2006-03-20 17:12:09 +00:00
}
2015-12-13 18:00:48 +00:00
}
2020-12-26 21:17:29 +00:00
}