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 .
*
*/
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"
# include "grdef.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"
2006-03-20 17:12:09 +00:00
# include "makesig.h"
2014-07-20 01:09:55 +00:00
# include "physfsx.h"
2008-02-24 14:41:27 +00:00
# include "gamefont.h"
2008-04-06 20:23:28 +00:00
# include "console.h"
2010-09-02 13:55:28 +00:00
# include "config.h"
2012-04-07 09:41:17 +00:00
# include "inferno.h"
2008-02-24 14:41:27 +00:00
# ifdef OGL
# include "ogl_init.h"
# endif
2014-07-20 03:48:27 +00:00
# include "compiler-array.h"
2014-11-25 04:03:13 +00:00
# include "compiler-range_for.h"
2014-08-16 04:15:16 +00:00
# include "compiler-make_unique.h"
2014-11-25 04:03:13 +00:00
# include "partial_range.h"
2014-07-20 03:48:27 +00:00
2008-10-29 11:03:31 +00:00
# define FONTSCALE_X(x) ((float)(x)*(FNTScaleX))
# define FONTSCALE_Y(x) ((float)(x)*(FNTScaleY))
2006-03-20 17:12:09 +00:00
# define MAX_OPEN_FONTS 50
2015-08-12 03:11:46 +00:00
namespace {
2013-12-22 22:03:07 +00:00
struct openfont
{
2014-07-20 03:48:27 +00:00
array < char , FILENAME_LEN > filename ;
// Unowned
2006-03-20 17:12:09 +00:00
grs_font * ptr ;
2014-07-20 03:48:27 +00:00
std : : unique_ptr < uint8_t [ ] > dataptr ;
2013-12-22 22:03:07 +00:00
} ;
2006-03-20 17:12:09 +00:00
2015-08-12 03:11:46 +00:00
}
2006-03-20 17:12:09 +00:00
//list of open fonts, for use (for now) for palette remapping
2014-07-20 03:48:27 +00:00
static array < openfont , MAX_OPEN_FONTS > open_font ;
2006-03-20 17:12:09 +00:00
# define BITS_TO_BYTES(x) (((x)+7)>>3)
2013-07-27 21:41:27 +00:00
static int gr_internal_string_clipped ( int x , int y , const char * s ) ;
static int gr_internal_string_clipped_m ( 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
while ( * p ! = 255 )
if ( p [ 0 ] = = first & & p [ 1 ] = = second )
return p ;
else p + = 3 ;
return NULL ;
}
//takes the character AFTER being offset into font
2015-05-09 17:38:59 +00:00
static inline bool INFONT ( const unsigned c )
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:38:59 +00:00
return c < = grd_curcanv - > cv_font - > ft_maxchar - grd_curcanv - > cv_font - > ft_minchar ;
2010-07-01 09:03:52 +00:00
}
2015-05-09 17:39:01 +00:00
namespace {
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-05-09 17:39:01 +00:00
static get_char_width_result < T > get_char_width ( 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 auto & cv_font = * grd_curcanv - > cv_font ;
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
if ( ! INFONT ( letter ) ) { //not in font, draw as space
2015-05-09 17:39:01 +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
}
2015-05-09 17:39:01 +00:00
const T width = proportional ? FONTSCALE_X ( cv_font . ft_widths [ letter ] ) : 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 )
2015-05-09 17:39:01 +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
}
2013-07-27 21:41:27 +00:00
static int get_centered_x ( const char * s )
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:39:01 +00:00
float w ;
2006-03-20 17:12:09 +00:00
for ( w = 0 ; * s ! = 0 & & * s ! = ' \n ' ; s + + ) {
if ( * s < = 0x06 ) {
if ( * s < = 0x03 )
s + + ;
continue ; //skip color codes.
}
2015-05-09 17:39:01 +00:00
w + = get_char_width < float > ( s [ 0 ] , s [ 1 ] ) . spacing ;
2006-03-20 17:12:09 +00:00
}
return ( ( grd_curcanv - > cv_bitmap . bm_w - w ) / 2 ) ;
}
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
2015-05-09 17:38:59 +00:00
const int gr_message_color_level = 1 ;
2012-04-09 09:30:40 +00:00
# define CHECK_EMBEDDED_COLORS() if ((*text_ptr >= 0x01) && (*text_ptr <= 0x02)) { \
2006-10-09 21:44:24 +00:00
text_ptr + + ; \
if ( * text_ptr ) { \
if ( gr_message_color_level > = * ( text_ptr - 1 ) ) \
2008-04-06 20:23:28 +00:00
grd_curcanv - > cv_font_fg_color = ( unsigned char ) * text_ptr ; \
2006-10-09 21:44:24 +00:00
text_ptr + + ; \
} \
} \
2012-04-09 09:30:40 +00:00
else if ( * text_ptr = = 0x03 ) \
{ \
underline = 1 ; \
text_ptr + + ; \
} \
2006-10-09 21:44:24 +00:00
else if ( ( * text_ptr > = 0x04 ) & & ( * text_ptr < = 0x06 ) ) { \
if ( gr_message_color_level > = * text_ptr - 3 ) \
2008-04-06 20:23:28 +00:00
grd_curcanv - > cv_font_fg_color = ( unsigned char ) 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 >
static int gr_internal_string0_template ( int x , int y , const char * s )
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:39:02 +00:00
const auto & cv_font = * grd_curcanv - > cv_font ;
const auto ft_flags = cv_font . ft_flags ;
const auto proportional = ft_flags & FT_PROPORTIONAL ;
2015-05-09 17:39:02 +00:00
const auto cv_font_bg_color = grd_curcanv - > 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
const auto orig_color = grd_curcanv - > cv_font_fg_color ;
2006-03-20 17:12:09 +00:00
VideoOffset1 = y * ROWSIZE + x ;
2015-05-09 17:39:02 +00:00
auto next_row = s ;
2006-03-20 17:12:09 +00:00
while ( next_row ! = NULL )
{
2015-05-09 17:39:02 +00:00
const auto text_ptr1 = next_row ;
2006-03-20 17:12:09 +00:00
next_row = NULL ;
if ( x = = 0x8000 ) { //centered
int xx = get_centered_x ( text_ptr1 ) ;
VideoOffset1 = y * ROWSIZE + xx ;
}
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 )
{
grd_curcanv - > 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 ;
const auto & result = get_char_width < int > ( 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
2015-05-09 17:39:01 +00:00
if ( masked_draws_background )
2014-11-25 04:03:13 +00:00
{
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 )
{
2006-03-20 17:12:09 +00:00
if ( underline )
2014-11-25 04:03:13 +00:00
{
std : : fill_n ( & DATA [ VideoOffset ] , width , grd_curcanv - > cv_font_fg_color ) ;
VideoOffset + = width ;
}
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-05-09 17:39:02 +00:00
const auto cv_font_fg_color = grd_curcanv - > cv_font_fg_color ;
2015-09-13 21:02:19 +00:00
auto data = & DATA [ VideoOffset ] ;
/* 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 ;
2015-05-09 17:39:02 +00:00
if ( ! 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
VideoOffset + = width ;
}
2006-03-20 17:12:09 +00:00
}
VideoOffset + = spacing - width ;
}
VideoOffset1 + = ROWSIZE ;
y + + ;
}
y + = skip_lines ;
VideoOffset1 + = ROWSIZE * skip_lines ;
skip_lines = 0 ;
}
return 0 ;
}
2015-05-09 17:39:01 +00:00
static int gr_internal_string0 ( int x , int y , const char * s )
{
return gr_internal_string0_template < true > ( x , y , s ) ;
}
static int gr_internal_string0m ( int x , int y , const char * s )
{
return gr_internal_string0_template < false > ( x , y , s ) ;
}
2006-03-20 17:12:09 +00:00
# ifndef OGL
2013-07-27 16:28:04 +00:00
static int gr_internal_color_string ( int x , int y , const char * s )
{
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 = { } ;
2015-07-25 23:10:47 +00:00
char_bm . set_type ( BM_LINEAR ) ;
2014-08-08 02:52:56 +00:00
char_bm . bm_flags = BM_FLAG_TRANSPARENT ;
2006-03-20 17:12:09 +00:00
unsigned char * fp ;
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 ;
2008-02-24 14:41:27 +00:00
char_bm . bm_h = grd_curcanv - > cv_font - > ft_h ; //set height for chars of this font
2006-03-20 17:12:09 +00:00
next_row = s ;
yy = y ;
2015-06-13 22:42:20 +00:00
const auto & & fspacy = FSPACY ( ) ;
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
xx = get_centered_x ( text_ptr ) ;
while ( * text_ptr )
{
if ( * text_ptr = = ' \n ' )
{
next_row = & text_ptr [ 1 ] ;
2015-06-13 22:42:20 +00:00
yy + = grd_curcanv - > cv_font - > ft_h + fspacy ( 1 ) ;
2006-03-20 17:12:09 +00:00
break ;
}
2008-02-24 14:41:27 +00:00
letter = ( unsigned char ) * text_ptr - grd_curcanv - > cv_font - > ft_minchar ;
2006-03-20 17:12:09 +00:00
2015-05-09 17:39:01 +00:00
const auto & result = get_char_width < int > ( text_ptr [ 0 ] , text_ptr [ 1 ] ) ;
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 ;
}
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_PROPORTIONAL )
fp = grd_curcanv - > cv_font - > ft_chars [ letter ] ;
2006-03-20 17:12:09 +00:00
else
2008-02-24 14:41:27 +00:00
fp = grd_curcanv - > cv_font - > ft_data + letter * BITS_TO_BYTES ( width ) * grd_curcanv - > cv_font - > ft_h ;
2006-03-20 17:12:09 +00:00
2014-11-30 22:09:20 +00:00
gr_init_bitmap ( char_bm , BM_LINEAR , 0 , 0 , width , grd_curcanv - > cv_font - > ft_h , width , fp ) ;
2014-12-02 03:35:01 +00:00
gr_bitmapm ( xx , yy , char_bm ) ;
2006-03-20 17:12:09 +00:00
xx + = spacing ;
text_ptr + + ;
}
}
return 0 ;
}
# 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
static void ogl_font_choose_size ( grs_font * font , int gap , int * rw , int * rh ) {
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 ;
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 ;
* rw = w ;
* rh = h ;
}
}
if ( smallr < = 0 )
Error ( " couldn't fit font? \n " ) ;
}
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 ;
2006-03-20 17:12:09 +00:00
int nchars = font - > ft_maxchar - font - > ft_minchar + 1 ;
2014-09-26 02:42:12 +00:00
int w , h , tw , th , curx = 0 , cury = 0 ;
2006-03-20 17:12:09 +00:00
unsigned char * fp ;
ubyte * data ;
2008-10-29 11:03:31 +00:00
int gap = 1 ; // x/y offset between the chars so we can filter
2012-06-30 20:38:21 +00:00
th = tw = 0xffff ;
2006-03-20 17:12:09 +00:00
ogl_font_choose_size ( font , gap , & tw , & th ) ;
2012-11-24 22:01:56 +00:00
MALLOC ( data , ubyte , tw * th ) ;
2008-10-29 11:03:31 +00:00
memset ( data , TRANSPARENCY_COLOR , tw * th ) ; // map the whole data with transparency so we won't have borders if using gap
2014-11-30 22:09:20 +00:00
gr_init_bitmap ( font - > ft_parent_bitmap , BM_LINEAR , 0 , 0 , tw , th , tw , 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
2014-08-16 04:15:16 +00:00
font - > ft_bitmaps = make_unique < grs_bitmap [ ] > ( nchars ) ;
2006-03-20 17:12:09 +00:00
h = font - > ft_h ;
2014-09-26 02:42:12 +00:00
for ( int i = 0 ; i < nchars ; i + + )
2008-10-29 11:03:31 +00:00
{
2006-03-20 17:12:09 +00:00
if ( font - > ft_flags & FT_PROPORTIONAL )
w = font - > ft_widths [ i ] ;
else
w = 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 )
{
2006-03-20 17:12:09 +00:00
if ( font - > ft_flags & FT_PROPORTIONAL )
fp = font - > ft_chars [ i ] ;
else
fp = font - > ft_data + i * w * h ;
2014-09-26 02:42:12 +00:00
for ( int y = 0 ; y < h ; y + + )
2008-10-29 11:03:31 +00:00
{
2014-09-26 02:42:12 +00:00
for ( int x = 0 ; x < w ; x + + )
2008-10-29 11:03:31 +00:00
{
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.
if ( gap & & i > = 99 & & i < = 102 )
{
// 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
if ( x = = 0 & & i ! = 99 & & i ! = 102 )
2014-12-02 03:24:38 +00:00
font - > ft_parent_bitmap . get_bitmap_data ( ) [ ( curx + x + ( cury + y ) * tw ) - 1 ] = fp [ x + y * w ] ;
2008-10-29 11:03:31 +00:00
// shift right border
if ( x = = w - 1 & & i ! = 100 )
2014-12-02 03:24:38 +00:00
font - > ft_parent_bitmap . get_bitmap_data ( ) [ ( curx + x + ( cury + y ) * tw ) + 1 ] = fp [ x + y * w ] ;
2008-10-29 11:03:31 +00:00
}
2006-03-20 17:12:09 +00:00
}
2008-10-29 11:03:31 +00:00
}
}
else
{
2006-03-20 17:12:09 +00:00
int BitMask , bits = 0 , white = gr_find_closest_color ( 63 , 63 , 63 ) ;
if ( font - > ft_flags & FT_PROPORTIONAL )
fp = font - > ft_chars [ i ] ;
else
fp = font - > ft_data + i * BITS_TO_BYTES ( w ) * h ;
2014-09-26 02:42:12 +00:00
for ( int y = 0 ; y < h ; y + + ) {
2006-03-20 17:12:09 +00:00
BitMask = 0 ;
2014-09-26 02:42:12 +00:00
for ( int x = 0 ; x < w ; x + + )
2006-03-20 17:12:09 +00:00
{
if ( BitMask = = 0 ) {
bits = * fp + + ;
BitMask = 0x80 ;
}
if ( bits & BitMask )
2014-12-02 03:24:38 +00:00
font - > ft_parent_bitmap . get_bitmap_data ( ) [ curx + x + ( cury + y ) * tw ] = white ;
2006-03-20 17:12:09 +00:00
else
2014-12-02 03:24:38 +00:00
font - > ft_parent_bitmap . get_bitmap_data ( ) [ curx + x + ( cury + y ) * tw ] = 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 ;
}
2014-11-13 03:42:41 +00:00
ogl_loadbmtexture_f ( font - > ft_parent_bitmap , GameCfg . TexFilt ) ;
2006-03-20 17:12:09 +00:00
}
2013-07-27 21:41:27 +00:00
static int ogl_internal_string ( int x , int y , const char * s )
2006-03-20 17:12:09 +00:00
{
2012-07-22 23:17:54 +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 ;
2008-02-24 14:41:27 +00:00
int orig_color = grd_curcanv - > cv_font_fg_color ; //to allow easy reseting to default string color with colored strings -MPM
2012-04-09 09:30:40 +00:00
int underline ;
2006-03-20 17:12:09 +00:00
next_row = s ;
yy = y ;
2015-07-25 23:10:47 +00:00
if ( grd_curscreen - > sc_canvas . cv_bitmap . get_type ( ) ! = BM_OGL )
2006-03-20 17:12:09 +00:00
Error ( " carp. \n " ) ;
2015-06-13 22:42:20 +00:00
const auto & & fspacy = FSPACY ( ) ;
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
xx = get_centered_x ( text_ptr ) ;
while ( * text_ptr )
{
2012-03-31 03:47:38 +00:00
int ft_w ;
2006-03-20 17:12:09 +00:00
if ( * text_ptr = = ' \n ' )
{
next_row = & text_ptr [ 1 ] ;
2015-06-13 22:42:20 +00:00
yy + = FONTSCALE_Y ( grd_curcanv - > cv_font - > ft_h ) + fspacy ( 1 ) ;
2006-03-20 17:12:09 +00:00
break ;
}
2008-02-24 14:41:27 +00:00
letter = ( unsigned char ) * text_ptr - grd_curcanv - > cv_font - > ft_minchar ;
2006-03-20 17:12:09 +00:00
2015-05-09 17:39:01 +00:00
const auto & result = get_char_width < int > ( text_ptr [ 0 ] , text_ptr [ 1 ] ) ;
const auto & spacing = result . spacing ;
2006-03-20 17:12:09 +00:00
2012-04-09 09:30:40 +00:00
underline = 0 ;
2010-07-01 09:03:52 +00:00
if ( ! INFONT ( letter ) | | ( unsigned char ) * text_ptr < = 0x06 ) //not in font, draw as space
{
2006-10-09 21:44:24 +00:00
CHECK_EMBEDDED_COLORS ( ) else {
xx + = spacing ;
text_ptr + + ;
}
2012-04-09 09:30:40 +00:00
if ( underline )
{
ubyte save_c = ( unsigned char ) COLOR ;
gr_setcolor ( grd_curcanv - > cv_font_fg_color ) ;
gr_rect ( xx , yy + grd_curcanv - > cv_font - > ft_baseline + 2 , xx + grd_curcanv - > cv_font - > ft_w , yy + grd_curcanv - > cv_font - > ft_baseline + 3 ) ;
gr_setcolor ( save_c ) ;
}
2006-03-20 17:12:09 +00:00
continue ;
}
2012-03-31 03:47:38 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_PROPORTIONAL )
ft_w = grd_curcanv - > cv_font - > ft_widths [ letter ] ;
else
ft_w = grd_curcanv - > cv_font - > ft_w ;
2012-03-30 12:36:35 +00:00
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_COLOR )
2014-11-30 22:09:18 +00:00
ogl_ubitmapm_cs ( xx , yy , FONTSCALE_X ( ft_w ) , FONTSCALE_Y ( grd_curcanv - > cv_font - > ft_h ) , grd_curcanv - > cv_font - > ft_bitmaps [ letter ] , - 1 , F1_0 ) ;
2006-03-20 17:12:09 +00:00
else {
2015-07-25 23:10:47 +00:00
if ( grd_curcanv - > cv_bitmap . get_type ( ) = = BM_OGL )
2014-11-30 22:09:18 +00:00
ogl_ubitmapm_cs ( xx , yy , ft_w * ( FONTSCALE_X ( grd_curcanv - > cv_font - > ft_w ) / grd_curcanv - > cv_font - > ft_w ) , FONTSCALE_Y ( grd_curcanv - > cv_font - > ft_h ) , grd_curcanv - > cv_font - > ft_bitmaps [ letter ] , grd_curcanv - > cv_font_fg_color , F1_0 ) ;
2006-03-20 17:12:09 +00:00
else
Error ( " ogl_internal_string: non-color string to non-ogl dest \n " ) ;
}
xx + = spacing ;
text_ptr + + ;
}
}
return 0 ;
}
2013-07-27 21:41:27 +00:00
static int gr_internal_color_string ( int x , int y , const char * s ) {
2006-03-20 17:12:09 +00:00
return ogl_internal_string ( x , y , s ) ;
}
# endif //OGL
2013-12-01 19:13:39 +00:00
void gr_string ( int x , int y , const char * s )
2006-03-20 17:12:09 +00:00
{
int w , h , aw ;
int clipped = 0 ;
2008-02-24 14:41:27 +00:00
Assert ( grd_curcanv - > cv_font ! = NULL ) ;
2006-03-20 17:12:09 +00:00
if ( x = = 0x8000 ) {
if ( y < 0 ) clipped | = 1 ;
gr_get_string_size ( s , & w , & h , & aw ) ;
// for x, since this will be centered, only look at
// width.
if ( w > grd_curcanv - > cv_bitmap . bm_w ) clipped | = 1 ;
if ( ( y + h ) > grd_curcanv - > cv_bitmap . bm_h ) clipped | = 1 ;
if ( ( y + h ) < 0 ) clipped | = 2 ;
if ( y > grd_curcanv - > cv_bitmap . bm_h ) clipped | = 2 ;
} else {
if ( ( x < 0 ) | | ( y < 0 ) ) clipped | = 1 ;
gr_get_string_size ( s , & w , & h , & aw ) ;
if ( ( x + w ) > grd_curcanv - > cv_bitmap . bm_w ) clipped | = 1 ;
if ( ( y + h ) > grd_curcanv - > cv_bitmap . bm_h ) clipped | = 1 ;
if ( ( x + w ) < 0 ) clipped | = 2 ;
if ( ( y + h ) < 0 ) clipped | = 2 ;
if ( x > grd_curcanv - > cv_bitmap . bm_w ) clipped | = 2 ;
if ( y > grd_curcanv - > cv_bitmap . bm_h ) clipped | = 2 ;
}
if ( ! clipped )
2013-12-01 19:13:39 +00:00
{
gr_ustring ( x , y , s ) ;
return ;
}
2006-03-20 17:12:09 +00:00
if ( clipped & 2 ) {
// Completely clipped...
2013-12-01 19:13:39 +00:00
return ;
2006-03-20 17:12:09 +00:00
}
// Partially clipped...
# ifdef OGL
if ( TYPE = = BM_OGL )
2013-12-01 19:13:39 +00:00
{
ogl_internal_string ( x , y , s ) ;
return ;
}
2006-03-20 17:12:09 +00:00
# endif
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_COLOR )
2013-12-01 19:13:39 +00:00
{
gr_internal_color_string ( x , y , s ) ;
return ;
}
2006-03-20 17:12:09 +00:00
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font_bg_color = = - 1 )
2013-12-01 19:13:39 +00:00
{
gr_internal_string_clipped_m ( x , y , s ) ;
return ;
}
2006-03-20 17:12:09 +00:00
2013-12-01 19:13:39 +00:00
gr_internal_string_clipped ( x , y , s ) ;
2006-03-20 17:12:09 +00:00
}
2013-12-01 19:13:39 +00:00
void gr_ustring ( int x , int y , const char * s )
2006-03-20 17:12:09 +00:00
{
# ifdef OGL
if ( TYPE = = BM_OGL )
2013-12-01 19:13:39 +00:00
{
ogl_internal_string ( x , y , s ) ;
return ;
}
2006-03-20 17:12:09 +00:00
# endif
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_COLOR ) {
2006-03-20 17:12:09 +00:00
2013-12-01 19:13:39 +00:00
gr_internal_color_string ( x , y , s ) ;
2006-03-20 17:12:09 +00:00
}
else
switch ( TYPE )
{
case BM_LINEAR :
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font_bg_color = = - 1 )
2013-12-01 19:13:39 +00:00
gr_internal_string0m ( x , y , s ) ;
2006-03-20 17:12:09 +00:00
else
2013-12-01 19:13:39 +00:00
gr_internal_string0 ( x , y , s ) ;
2006-03-20 17:12:09 +00:00
}
2013-12-01 19:13:39 +00:00
return ;
2006-03-20 17:12:09 +00:00
}
2012-07-22 23:17:54 +00:00
void gr_get_string_size ( const char * s , int * string_width , int * string_height , int * average_width )
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 ;
const auto & cv_font = * grd_curcanv - > cv_font ;
* average_width = cv_font . ft_w ;
2006-03-20 17:12:09 +00:00
if ( s ! = NULL )
{
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 ;
2006-03-20 17:12:09 +00:00
}
2015-05-09 17:39:01 +00:00
const auto & result = get_char_width < float > ( s [ 0 ] , s [ 1 ] ) ;
const auto & spacing = result . spacing ;
2010-07-01 09:03:52 +00:00
string_width_f + = spacing ;
s + + ;
2006-03-20 17:12:09 +00:00
}
}
2015-06-13 22:42:20 +00:00
* string_width = std : : max ( longest_width , string_width_f ) ;
const auto fontscale_y = FONTSCALE_Y ( cv_font . ft_h ) ;
const float string_height_f = fontscale_y + ( lines * ( fontscale_y + FSPACY ( 1 ) ) ) ;
2010-07-01 09:03:52 +00:00
* string_height = string_height_f ;
2006-03-20 17:12:09 +00:00
}
2013-12-08 23:37:40 +00:00
void ( gr_uprintf ) ( int x , int y , const char * format , . . . )
2006-03-20 17:12:09 +00:00
{
char buffer [ 1000 ] ;
va_list args ;
va_start ( args , format ) ;
2014-06-01 17:08:17 +00:00
vsprintf ( buffer , format , args ) ;
2013-12-01 18:27:54 +00:00
va_end ( args ) ;
2013-12-01 19:13:39 +00:00
gr_ustring ( x , y , buffer ) ;
2006-03-20 17:12:09 +00:00
}
2013-12-08 23:37:40 +00:00
void ( gr_printf ) ( int x , int y , const char * format , . . . )
2006-03-20 17:12:09 +00:00
{
char buffer [ 1000 ] ;
va_list args ;
va_start ( args , format ) ;
2014-06-01 17:08:17 +00:00
vsprintf ( buffer , format , args ) ;
2013-12-01 18:27:54 +00:00
va_end ( args ) ;
2013-12-01 19:13:39 +00:00
gr_string ( x , y , buffer ) ;
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
2014-07-20 03:48:27 +00:00
auto e = end ( open_font ) ;
auto i = std : : find_if ( begin ( open_font ) , e , [ & font ] ( const openfont & o ) { return o . ptr = = font . get ( ) ; } ) ;
if ( i = = e )
throw std : : logic_error ( " closing non-open font " ) ;
2006-03-20 17:12:09 +00:00
# ifdef OGL
2014-11-30 22:09:20 +00:00
gr_free_bitmap_data ( font - > ft_parent_bitmap ) ;
2006-03-20 17:12:09 +00:00
# endif
2014-07-20 03:48:27 +00:00
auto & f = * i ;
f . dataptr . reset ( ) ;
2014-10-01 02:14:41 +00:00
f . ptr = nullptr ;
2006-03-20 17:12:09 +00:00
}
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-06-08 22:24:17 +00:00
//remap a font, re-reading its data & palette
2014-07-20 03:48:27 +00:00
static void gr_remap_font ( grs_font * font , const char * fontname , uint8_t * font_data ) ;
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 ( )
{
2014-11-25 04:03:13 +00:00
range_for ( auto & i , open_font )
{
auto font = i . ptr ;
2006-03-20 17:12:09 +00:00
if ( font & & ( font - > ft_flags & FT_COLOR ) )
2014-11-25 04:03:13 +00:00
gr_remap_font ( font , & i . filename [ 0 ] , i . dataptr . get ( ) ) ;
2006-03-20 17:12:09 +00:00
}
}
void gr_remap_mono_fonts ( )
{
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " gr_remap_mono_fonts () " ) ;
2014-11-25 04:03:13 +00:00
range_for ( auto & i , open_font )
{
auto font = i . ptr ;
2006-03-20 17:12:09 +00:00
if ( font & & ! ( font - > ft_flags & FT_COLOR ) )
2014-11-25 04:03:13 +00:00
gr_remap_font ( font , & i . filename [ 0 ] , i . dataptr . get ( ) ) ;
2006-03-20 17:12:09 +00:00
}
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
/*
2011-06-01 07:59:55 +00:00
* reads a grs_font structure from a PHYSFS_file
2006-03-20 17:12:09 +00:00
*/
2012-11-02 17:30:37 +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 ) ;
gf - > ft_bytewidth = PHYSFSX_readShort ( fp ) ;
2012-11-02 17:30:37 +00:00
gf - > ft_data = ( ubyte * ) ( ( size_t ) PHYSFSX_readInt ( fp ) - GRS_FONT_SIZE ) ;
2014-08-07 03:05:48 +00:00
PHYSFSX_readInt ( fp ) ;
2012-11-02 17:30:37 +00:00
gf - > ft_widths = ( short * ) ( ( size_t ) PHYSFSX_readInt ( fp ) - GRS_FONT_SIZE ) ;
gf - > ft_kerndata = ( ubyte * ) ( ( size_t ) PHYSFSX_readInt ( fp ) - GRS_FONT_SIZE ) ;
2006-03-20 17:12:09 +00:00
}
2014-07-20 03:48:27 +00:00
grs_font_ptr gr_init_font ( const char * fontname )
2006-03-20 17:12:09 +00:00
{
unsigned char * ptr ;
int nchars ;
char file_id [ 4 ] ;
int datasize ; //size up to (but not including) palette
//find free font slot
2014-07-20 17:16:02 +00:00
auto e = end ( open_font ) ;
auto i = std : : find_if ( begin ( open_font ) , e , [ ] ( const openfont & o ) { return o . ptr = = NULL ; } ) ;
if ( i = = e )
throw std : : logic_error ( " too many open fonts " ) ;
auto & f = * i ;
2006-03-20 17:12:09 +00:00
2014-07-20 17:16:02 +00:00
strncpy ( & f . filename [ 0 ] , fontname , FILENAME_LEN ) ;
2006-03-20 17:12:09 +00:00
2015-01-17 18:31:42 +00:00
auto fontfile = PHYSFSX_openReadBuffered ( fontname ) ;
2006-03-20 17:12:09 +00:00
if ( ! fontfile ) {
2013-12-07 00:47:27 +00:00
con_printf ( CON_VERBOSE , " Can't open font file %s " , fontname ) ;
2014-07-20 03:48:27 +00:00
return { } ;
2006-03-20 17:12:09 +00:00
}
2011-06-01 07:59:55 +00:00
PHYSFS_read ( fontfile , file_id , 4 , 1 ) ;
2012-11-02 17:30:37 +00:00
if ( memcmp ( file_id , " PSFN " , 4 ) ) {
2013-12-07 00:47:27 +00:00
con_printf ( CON_NORMAL , " File %s is not a font file " , fontname ) ;
2014-07-20 03:48:27 +00:00
return { } ;
2006-03-20 17:12:09 +00:00
}
2011-06-01 07:59:55 +00:00
datasize = PHYSFSX_readInt ( fontfile ) ;
2006-03-20 17:12:09 +00:00
datasize - = GRS_FONT_SIZE ; // subtract the size of the header.
2014-08-16 04:15:16 +00:00
auto font = 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
2014-08-16 04:15:16 +00:00
auto font_data = make_unique < uint8_t [ ] > ( datasize ) ;
2011-06-01 07:59:55 +00:00
PHYSFS_read ( fontfile , font_data , 1 , datasize ) ;
2006-03-20 17:12:09 +00:00
nchars = font - > ft_maxchar - font - > ft_minchar + 1 ;
if ( font - > ft_flags & FT_PROPORTIONAL ) {
2007-06-14 12:32:18 +00:00
font - > ft_widths = ( short * ) & font_data [ ( size_t ) font - > ft_widths ] ;
font - > ft_data = ( unsigned char * ) & font_data [ ( size_t ) font - > ft_data ] ;
2014-08-16 04:15:16 +00:00
font - > ft_chars = make_unique < uint8_t * [ ] > ( nchars ) ;
2006-03-20 17:12:09 +00:00
ptr = font - > ft_data ;
2014-07-20 17:16:02 +00:00
for ( int i = 0 ; i < nchars ; i + + ) {
2006-03-20 17:12:09 +00:00
font - > ft_widths [ i ] = INTEL_SHORT ( font - > ft_widths [ i ] ) ;
font - > ft_chars [ i ] = ptr ;
if ( font - > ft_flags & FT_COLOR )
ptr + = font - > ft_widths [ i ] * font - > ft_h ;
else
ptr + = BITS_TO_BYTES ( font - > ft_widths [ i ] ) * font - > ft_h ;
}
} else {
2014-07-20 03:48:27 +00:00
font - > ft_data = font_data . get ( ) ;
2014-08-07 03:05:48 +00:00
font - > ft_chars . reset ( ) ;
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 )
2007-06-14 12:32:18 +00:00
font - > ft_kerndata = ( unsigned char * ) & font_data [ ( size_t ) font - > ft_kerndata ] ;
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 ;
2014-11-30 22:09:21 +00:00
array < uint8_t , 256 > colormap ;
array < unsigned , 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
2013-01-06 21:03:57 +00:00
build_colormap_good ( palette , colormap , freq ) ;
2006-03-20 17:12:09 +00:00
colormap [ TRANSPARENCY_COLOR ] = TRANSPARENCY_COLOR ; // changed from colormap[255] = 255 to this for macintosh
2011-01-19 20:35:14 +00:00
decode_data ( font - > 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
2014-07-20 03:48:27 +00:00
grd_curcanv - > cv_font = font . get ( ) ;
2008-02-24 14:41:27 +00:00
grd_curcanv - > cv_font_fg_color = 0 ;
grd_curcanv - > cv_font_bg_color = 0 ;
2006-03-20 17:12:09 +00:00
# ifdef OGL
2014-07-20 03:48:27 +00:00
ogl_init_font ( font . get ( ) ) ;
2006-03-20 17:12:09 +00:00
# endif
2014-07-20 17:16:02 +00:00
f . ptr = font . get ( ) ;
f . dataptr = move ( font_data ) ;
2014-07-20 03:48:27 +00:00
return grs_font_ptr ( font . release ( ) ) ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//remap a font by re-reading its data & palette
2014-07-20 03:48:27 +00:00
void gr_remap_font ( grs_font * font , const char * fontname , uint8_t * font_data )
2006-03-20 17:12:09 +00:00
{
int nchars ;
char file_id [ 4 ] ;
int datasize ; //size up to (but not including) palette
unsigned char * ptr ;
if ( ! ( font - > ft_flags & FT_COLOR ) )
return ;
2015-01-17 18:31:42 +00:00
auto fontfile = PHYSFSX_openReadBuffered ( fontname ) ;
2006-03-20 17:12:09 +00:00
if ( ! fontfile )
Error ( " Can't open font file %s " , fontname ) ;
2011-06-01 07:59:55 +00:00
PHYSFS_read ( fontfile , file_id , 4 , 1 ) ;
2006-03-20 17:12:09 +00:00
if ( ! strncmp ( file_id , " NFSP " , 4 ) )
Error ( " File %s is not a font file " , fontname ) ;
2011-06-01 07:59:55 +00:00
datasize = PHYSFSX_readInt ( fontfile ) ;
2006-03-20 17:12:09 +00:00
datasize - = GRS_FONT_SIZE ; // subtract the size of the header.
2014-08-07 03:05:48 +00:00
font - > ft_chars . reset ( ) ;
2006-03-20 17:12:09 +00:00
grs_font_read ( font , fontfile ) ; // have to reread in case mission hogfile overrides font.
2011-06-01 07:59:55 +00:00
PHYSFS_read ( fontfile , font_data , 1 , datasize ) ; //read raw data
2006-03-20 17:12:09 +00:00
nchars = font - > ft_maxchar - font - > ft_minchar + 1 ;
if ( font - > ft_flags & FT_PROPORTIONAL ) {
2007-06-14 12:32:18 +00:00
font - > ft_widths = ( short * ) & font_data [ ( size_t ) font - > ft_widths ] ;
font - > ft_data = ( unsigned char * ) & font_data [ ( size_t ) font - > ft_data ] ;
2014-08-16 04:15:16 +00:00
font - > ft_chars = make_unique < uint8_t * [ ] > ( nchars ) ;
2006-03-20 17:12:09 +00:00
ptr = font - > ft_data ;
2014-09-26 02:42:12 +00:00
for ( int i = 0 ; i < nchars ; i + + ) {
2006-03-20 17:12:09 +00:00
font - > ft_widths [ i ] = INTEL_SHORT ( font - > ft_widths [ i ] ) ;
font - > ft_chars [ i ] = ptr ;
if ( font - > ft_flags & FT_COLOR )
ptr + = font - > ft_widths [ i ] * font - > ft_h ;
else
ptr + = BITS_TO_BYTES ( font - > ft_widths [ i ] ) * font - > ft_h ;
}
} else {
font - > ft_data = ( unsigned char * ) font_data ;
2014-08-07 03:05:48 +00:00
font - > ft_chars . reset ( ) ;
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 )
2007-06-14 12:32:18 +00:00
font - > ft_kerndata = ( unsigned char * ) & font_data [ ( size_t ) font - > ft_kerndata ] ;
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 ;
2014-11-30 22:09:21 +00:00
array < uint8_t , 256 > colormap ;
array < unsigned , 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
2013-01-06 21:03:57 +00:00
build_colormap_good ( palette , colormap , freq ) ;
2006-03-20 17:12:09 +00:00
colormap [ TRANSPARENCY_COLOR ] = TRANSPARENCY_COLOR ; // changed from colormap[255] = 255 to this for macintosh
2011-01-19 20:35:14 +00:00
decode_data ( font - > ft_data , ptr - font - > ft_data , colormap , freq ) ;
2006-03-20 17:12:09 +00:00
}
# ifdef OGL
2014-11-30 22:09:20 +00:00
gr_free_bitmap_data ( font - > ft_parent_bitmap ) ;
2006-03-20 17:12:09 +00:00
ogl_init_font ( font ) ;
# endif
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2014-07-20 03:48:27 +00:00
void gr_set_curfont ( const grs_font * n )
2006-03-20 17:12:09 +00:00
{
2012-11-18 18:21:50 +00:00
grd_curcanv - > cv_font = n ;
2006-03-20 17:12:09 +00:00
}
2008-02-24 14:41:27 +00:00
void gr_set_fontcolor ( int fg_color , int bg_color )
2006-03-20 17:12:09 +00:00
{
2008-02-24 14:41:27 +00:00
grd_curcanv - > cv_font_fg_color = fg_color ;
grd_curcanv - > cv_font_bg_color = bg_color ;
2006-03-20 17:12:09 +00:00
}
2015-05-09 17:38:59 +00:00
template < bool masked_draws_background >
static int gr_internal_string_clipped_template ( int x , int y , const char * s )
2006-03-20 17:12:09 +00:00
{
2012-07-22 23:17:54 +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 x1 = x , last_x ;
next_row = s ;
2015-05-09 17:38:59 +00:00
const auto & cv_font = * grd_curcanv - > 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 ;
2015-05-09 17:39:00 +00:00
const auto cv_font_bg_color = grd_curcanv - > cv_font_bg_color ;
2015-05-09 17:38:59 +00:00
2006-03-20 17:12:09 +00:00
while ( next_row ! = NULL )
{
text_ptr1 = next_row ;
next_row = NULL ;
x = x1 ;
if ( x = = 0x8000 ) //centered
x = get_centered_x ( text_ptr1 ) ;
last_x = x ;
2015-05-09 17:38:59 +00:00
for ( int r = 0 ; r < cv_font . ft_h ; r + + ) {
2006-03-20 17:12:09 +00:00
text_ptr = text_ptr1 ;
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 )
{
grd_curcanv - > 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-05-09 17:39:01 +00:00
const auto & result = get_char_width < int > ( c , text_ptr [ 1 ] ) ;
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 ;
}
2015-05-09 17:39:00 +00:00
const auto cv_font_fg_color = grd_curcanv - > cv_font_fg_color ;
gr_setcolor ( 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 - - ; )
{
2006-03-20 17:12:09 +00:00
gr_pixel ( x + + , y ) ;
}
} 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-13 21:02:19 +00:00
const auto bit_enabled = ( bits & 1 ) ;
bits > > = 1 ;
2015-05-09 17:39:00 +00:00
if ( masked_draws_background )
gr_setcolor ( 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 ;
}
gr_pixel ( x , y ) ;
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 ;
}
2013-07-27 21:41:27 +00:00
static int gr_internal_string_clipped_m ( int x , int y , const char * s )
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:38:59 +00:00
return gr_internal_string_clipped_template < true > ( x , y , s ) ;
}
2006-03-20 17:12:09 +00:00
2015-05-09 17:38:59 +00:00
static int gr_internal_string_clipped ( int x , int y , const char * s )
{
return gr_internal_string_clipped_template < false > ( x , y , s ) ;
2006-03-20 17:12:09 +00:00
}