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"
# 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"
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"
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 {
2015-11-14 18:17:21 +00:00
const uint8_t kerndata_terminator = 255 ;
2015-08-12 03:11:46 +00:00
}
2015-12-13 18:00:49 +00:00
namespace dcx {
2015-12-13 18:00:48 +00:00
2006-03-20 17:12:09 +00:00
//list of open fonts, for use (for now) for palette remapping
2016-01-10 21:18:09 +00:00
static array < grs_font * , 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
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
2015-05-09 17:38:59 +00:00
static inline bool INFONT ( const unsigned c )
2006-03-20 17:12:09 +00:00
{
2015-12-22 04:18:50 +00:00
const auto & cv_font = * grd_curcanv - > cv_font ;
return c < = cv_font . ft_maxchar - 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-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
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-12-22 04:18:50 +00:00
const auto & cv_font = * grd_curcanv - > cv_font ;
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-12-22 04:18:50 +00:00
w + = get_char_width < float > ( cv_font , 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 ;
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
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 ;
2015-12-22 04:18:50 +00:00
const auto & cv_font = * grd_curcanv - > cv_font ;
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 ;
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-12-22 04:18:50 +00:00
yy + = cv_font . ft_h + fspacy ( 1 ) ;
2006-03-20 17:12:09 +00:00
break ;
}
2015-12-22 04:18:50 +00:00
letter = ( unsigned char ) * 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 ;
}
2015-12-22 04:18:50 +00:00
if ( cv_font . ft_flags & FT_PROPORTIONAL )
fp = cv_font . ft_chars [ letter ] ;
2006-03-20 17:12:09 +00:00
else
2015-12-22 04:18:50 +00:00
fp = & cv_font . ft_data [ letter * BITS_TO_BYTES ( width ) * cv_font . ft_h ] ;
2006-03-20 17:12:09 +00:00
2015-12-22 04:18:50 +00:00
gr_init_bitmap ( char_bm , BM_LINEAR , 0 , 0 , width , 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 ;
}
2015-12-18 04:08:23 +00:00
ogl_loadbmtexture_f ( font - > ft_parent_bitmap , CGameCfg . 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 ( ) ;
2015-12-22 04:18:50 +00:00
const auto & cv_font = * grd_curcanv - > cv_font ;
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-12-22 04:18:50 +00:00
yy + = FONTSCALE_Y ( cv_font . ft_h ) + fspacy ( 1 ) ;
2006-03-20 17:12:09 +00:00
break ;
}
2015-12-22 04:18:50 +00:00
letter = ( unsigned char ) * 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 & 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 ) ;
2015-12-22 04:18:50 +00:00
gr_rect ( xx , yy + cv_font . ft_baseline + 2 , xx + cv_font . ft_w , yy + cv_font . ft_baseline + 3 ) ;
2012-04-09 09:30:40 +00:00
gr_setcolor ( save_c ) ;
}
2006-03-20 17:12:09 +00:00
continue ;
}
2012-03-31 03:47:38 +00:00
2015-12-22 04:18:50 +00:00
if ( cv_font . ft_flags & FT_PROPORTIONAL )
ft_w = cv_font . ft_widths [ letter ] ;
2012-03-31 03:47:38 +00:00
else
2015-12-22 04:18:50 +00:00
ft_w = cv_font . ft_w ;
2012-03-30 12:36:35 +00:00
2015-12-22 04:18:50 +00:00
if ( cv_font . ft_flags & FT_COLOR )
ogl_ubitmapm_cs ( xx , yy , FONTSCALE_X ( ft_w ) , FONTSCALE_Y ( cv_font . ft_h ) , 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 )
2015-12-22 04:18:50 +00:00
ogl_ubitmapm_cs ( xx , yy , ft_w * ( FONTSCALE_X ( cv_font . ft_w ) / cv_font . ft_w ) , FONTSCALE_Y ( cv_font . ft_h ) , 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
2015-09-29 02:41:22 +00:00
void gr_string ( const int x , const int y , const char * const s )
2015-09-29 02:41:22 +00:00
{
int w , h ;
gr_get_string_size ( s , & w , & h , nullptr ) ;
gr_string ( x , y , s , w , h ) ;
}
2016-01-10 21:18:09 +00:00
static void gr_ustring_mono ( int x , int y , const char * s )
2006-03-20 17:12:09 +00:00
{
2016-01-10 21:18:09 +00:00
switch ( TYPE )
{
case BM_LINEAR :
if ( grd_curcanv - > cv_font_bg_color = = - 1 )
gr_internal_string0m ( x , y , s ) ;
else
gr_internal_string0 ( x , y , s ) ;
}
}
2006-03-20 17:12:09 +00:00
2016-01-10 21:18:09 +00:00
void gr_string ( const int x , const int y , const char * const s , const int w , const int h )
{
2008-02-24 14:41:27 +00:00
Assert ( grd_curcanv - > cv_font ! = NULL ) ;
2006-03-20 17:12:09 +00:00
2015-09-29 02:41:22 +00:00
const auto bm_h = grd_curcanv - > cv_bitmap . bm_h ;
if ( y > bm_h )
return ;
const auto bm_w = grd_curcanv - > 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 ) {
2015-09-29 02:41:22 +00:00
if ( y + h < 0 )
return ;
2006-03-20 17:12:09 +00:00
// 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 ;
if ( x + w < 0 | |
y + h < 0 )
return ;
2016-01-10 21:18:09 +00:00
xw + = x ;
2013-12-01 19:13:39 +00:00
}
2016-01-10 21:18:09 +00:00
if (
2006-03-20 17:12:09 +00:00
# ifdef OGL
2016-01-10 21:18:09 +00:00
TYPE = = BM_OGL | |
# endif
grd_curcanv - > cv_font - > ft_flags & FT_COLOR )
2013-12-01 19:13:39 +00:00
{
2016-01-10 21:18:09 +00:00
gr_internal_color_string ( 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
{
2016-01-10 21:18:09 +00:00
gr_ustring_mono ( x , y , s ) ;
2013-12-01 19:13:39 +00:00
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 ) {
2013-12-01 19:13:39 +00:00
gr_internal_color_string ( x , y , s ) ;
2006-03-20 17:12:09 +00:00
}
else
2016-01-10 21:18:09 +00:00
gr_ustring_mono ( x , y , s ) ;
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 ;
2015-09-29 02:41:22 +00:00
if ( average_width )
* average_width = cv_font . ft_w ;
if ( ! string_width & & ! string_height )
return ;
if ( s )
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 ;
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 + + ;
2006-03-20 17:12:09 +00:00
}
}
2015-09-29 02:41:22 +00:00
if ( string_width )
* string_width = std : : max ( longest_width , string_width_f ) ;
if ( string_height )
{
const auto fontscale_y = FONTSCALE_Y ( cv_font . ft_h ) ;
* string_height = static_cast < int > ( static_cast < float > ( fontscale_y + ( lines * ( fontscale_y + FSPACY ( 1 ) ) ) ) ) ;
}
2006-03-20 17:12:09 +00:00
}
2015-12-22 04:18:52 +00:00
std : : pair < const char * , unsigned > gr_get_string_wrap ( const char * s , unsigned limit )
{
assert ( s ) ;
float string_width_f = 0.0 ;
const float limit_f = limit ;
const auto & cv_font = * grd_curcanv - > cv_font ;
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
2016-01-10 21:18:08 +00:00
template < void ( & F ) ( int , int , const char * ) >
void ( gr_printt ) ( 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 ) ;
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 ) ;
2016-01-10 21:18:08 +00:00
F ( x , y , buffer ) ;
2006-03-20 17:12:09 +00:00
}
2016-01-10 21:18:08 +00:00
template void gr_printt < gr_string > ( int , int , const char * , . . . ) ;
template void gr_printt < gr_ustring > ( 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 )
{
2016-01-10 21:18:09 +00:00
# ifdef OGL
gr_free_bitmap_data ( font - > ft_parent_bitmap ) ;
# endif
2006-03-20 17:12:09 +00:00
//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
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 ( )
{
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-01-10 21:18:09 +00:00
gr_remap_font ( font , & font - > ft_filename [ 0 ] , font - > ft_allocdata . get ( ) ) ;
2006-03-20 17:12:09 +00:00
}
}
2015-12-13 18:00:48 +00:00
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
}
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 ;
2015-11-14 18:17:21 +00:00
struct {
array < char , 4 > magic ;
unsigned datasize ; //size up to (but not including) palette
} file_header ;
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
}
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
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 ) ;
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
2015-11-14 18:17:21 +00:00
unsigned nchars ;
2006-03-20 17:12:09 +00:00
nchars = font - > ft_maxchar - font - > ft_minchar + 1 ;
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 { } ;
}
font - > ft_data = reinterpret_cast < unsigned char * > ( & font_data [ offset_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 ;
2015-10-09 02:46:09 +00:00
const unsigned is_color = font - > ft_flags & FT_COLOR ;
const unsigned ft_h = font - > ft_h ;
std : : generate_n ( font - > ft_chars . get ( ) , nchars , [ is_color , ft_h , & w , & ptr ] {
const unsigned s = INTEL_SHORT ( * w ) ;
if ( words_bigendian )
* 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 {
2014-07-20 03:48:27 +00:00
font - > ft_data = font_data . get ( ) ;
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 ;
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
2016-01-10 21:18:09 +00:00
auto & ft_filename = font - > ft_filename ;
font - > ft_allocdata = move ( font_data ) ;
strncpy ( & ft_filename [ 0 ] , fontname , ft_filename . size ( ) ) ;
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 ( ) ;
}
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
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 ;
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
2015-12-13 18:00:48 +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-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 ;
}
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-24 03:05:16 +00:00
const auto bit_enabled = ( bits & 0x80 ) ;
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
}
2015-12-13 18:00:48 +00:00
}