2012-10-26 10:45:51 +02:00
// Sprite packing method borrowed from glorp engine and heavily modified.
// For license safety, just run this as a build tool, don't build it into your game/program.
// https://github.com/zorbathut/glorp
2017-03-18 16:18:39 +01:00
// Horrible build instructions:
// * Download freetype, put in ppsspp/ext as freetype/
// * Open tools.sln
// * In Code Generation on freetype, change from Multithreaded DLL to Multithreaded.
// * Build
// * Move exe file to ext/native/tools/build
2012-10-26 10:45:51 +02:00
// data we need to provide:
// sx, sy
// dx, dy
// ox, oy
// wx
// line height
// dist-per-pixel
2014-07-15 17:24:15 +02:00
# include <libpng17/png.h>
2012-10-26 10:45:51 +02:00
# include <ft2build.h>
2017-03-18 16:18:39 +01:00
# include <freetype/ftbitmap.h>
2013-04-21 11:51:58 -07:00
# include <set>
# include <map>
2012-10-26 10:45:51 +02:00
# include <vector>
# include <algorithm>
# include <string>
# include <cmath>
2014-07-15 17:24:15 +02:00
# include "base/logging.h"
2013-04-17 12:18:32 +02:00
# include "gfx/texture_atlas.h"
2012-10-26 10:45:51 +02:00
# include "image/png_load.h"
# include "image/zim_save.h"
2013-04-18 14:40:14 +02:00
# include "kanjifilter.h"
2013-05-05 06:25:00 +09:00
// extracted only JIS Kanji on the CJK Unified Ideographs of UCS2. Cannot reading BlockAllocator. (texture size over)
//#define USE_KANJI KANJI_STANDARD | KANJI_RARELY_USED | KANJI_LEVEL4
2013-05-02 01:08:07 +09:00
// daily-use character only. However, it is too enough this.
2013-05-05 06:25:00 +09:00
//#define USE_KANJI KANJI_STANDARD (texture size over)
// Shift-JIS filtering. (texture size over)
//#define USE_KANJI KANJI_SJIS_L1 | KANJI_SJIS_L2
2013-05-02 01:08:07 +09:00
// more conpact daily-use character. but, not enough this.
// if when you find the unintelligible sequence of characters,
// add kanjiFilter Array with KANJI_LEARNING_ORDER_ADDTIONAL.
2013-05-02 00:22:59 +09:00
# define USE_KANJI KANJI_LEARNING_ORDER_ALL
2013-05-05 06:25:00 +09:00
2013-04-19 23:54:38 +02:00
# include "util/text/utf8.h"
2013-04-18 14:40:14 +02:00
2012-10-26 10:45:51 +02:00
using namespace std ;
static int global_id ;
2013-11-17 15:34:51 +10:00
static bool etc1 = false ;
2012-10-26 10:45:51 +02:00
static bool highcolor = false ;
2013-04-18 14:40:14 +02:00
typedef unsigned short u16 ;
struct CharRange : public AtlasCharRange {
std : : set < u16 > filter ;
} ;
2012-10-26 10:45:51 +02:00
enum Effect {
FX_COPY = 0 ,
FX_RED_TO_ALPHA_SOLID_WHITE = 1 , // for alpha fonts
FX_RED_TO_INTENSITY_ALPHA_255 = 2 ,
FX_PREMULTIPLY_ALPHA = 3 ,
FX_PINK_TO_ALPHA = 4 , // for alpha fonts
FX_INVALID = 5 ,
} ;
const char * effect_str [ 5 ] = {
" copy " , " r2a " , " r2i " , " pre " , " p2a " ,
} ;
Effect GetEffect ( const char * text ) {
for ( int i = 0 ; i < 5 ; i + + ) {
if ( ! strcmp ( text , effect_str [ i ] ) ) {
return ( Effect ) i ;
}
}
return FX_INVALID ;
}
2013-04-21 11:51:58 -07:00
struct FontReference {
2013-04-21 21:53:46 +02:00
FontReference ( string name , string file , vector < CharRange > ranges , int pixheight , float vertOffset )
: name_ ( name ) , file_ ( file ) , ranges_ ( ranges ) , size_ ( pixheight ) , vertOffset_ ( vertOffset ) {
2013-04-21 11:51:58 -07:00
}
string name_ ;
string file_ ;
vector < CharRange > ranges_ ;
int size_ ;
2013-04-21 21:53:46 +02:00
float vertOffset_ ;
2013-04-21 11:51:58 -07:00
} ;
typedef vector < FontReference > FontReferenceList ;
2012-10-26 10:45:51 +02:00
template < class T >
struct Image {
vector < vector < T > > dat ;
void resize ( int x , int y ) {
dat . resize ( y ) ;
for ( int i = 0 ; i < y ; i + + )
dat [ i ] . resize ( x ) ;
}
int width ( ) const {
return ( int ) dat [ 0 ] . size ( ) ;
}
int height ( ) const {
return ( int ) dat . size ( ) ;
}
void copyfrom ( const Image & img , int ox , int oy , int effect ) {
CHECK ( img . dat [ 0 ] . size ( ) + ox < = dat [ 0 ] . size ( ) ) ;
CHECK ( img . dat . size ( ) + oy < = dat . size ( ) ) ;
for ( int y = 0 ; y < ( int ) img . dat . size ( ) ; y + + ) {
for ( int x = 0 ; x < ( int ) img . dat [ y ] . size ( ) ; x + + ) {
switch ( effect ) {
case FX_COPY :
dat [ y + oy ] [ ox + x ] = img . dat [ y ] [ x ] ;
break ;
case FX_RED_TO_ALPHA_SOLID_WHITE :
dat [ y + oy ] [ ox + x ] = 0x00FFFFFF | ( img . dat [ y ] [ x ] < < 24 ) ;
break ;
case FX_RED_TO_INTENSITY_ALPHA_255 :
dat [ y + oy ] [ ox + x ] = 0xFF000000 | img . dat [ y ] [ x ] | ( img . dat [ y ] [ x ] < < 8 ) | ( img . dat [ y ] [ x ] < < 16 ) ;
break ;
case FX_PREMULTIPLY_ALPHA :
{
unsigned int color = img . dat [ y ] [ x ] ;
unsigned int a = color > > 24 ;
unsigned int r = ( color & 0xFF ) * a > > 8 , g = ( color & 0xFF00 ) * a > > 8 , b = ( color & 0xFF0000 ) * a > > 8 ;
color = ( color & 0xFF000000 ) | ( r & 0xFF ) | ( g & 0xFF00 ) | ( b & 0xFF0000 ) ;
// Simulate 4444
color = color & 0xF0F0F0F0 ;
color | = color > > 4 ;
dat [ y + oy ] [ ox + x ] = color ;
break ;
}
case FX_PINK_TO_ALPHA :
dat [ y + oy ] [ ox + x ] = ( ( img . dat [ y ] [ x ] & 0xFFFFFF ) = = 0xFF00FF ) ? 0x00FFFFFF : ( img . dat [ y ] [ x ] | 0xFF000000 ) ;
break ;
default :
dat [ y + oy ] [ ox + x ] = 0xFFFF00FF ;
break ;
}
}
}
}
void set ( int sx , int sy , int ex , int ey , unsigned char fil ) {
for ( int y = sy ; y < ey ; y + + )
fill ( dat [ y ] . begin ( ) + sx , dat [ y ] . begin ( ) + ex , fil ) ;
}
bool LoadPNG ( const char * png_name ) {
unsigned char * img_data ;
int w , h ;
if ( 1 ! = pngLoad ( png_name , & w , & h , & img_data , false ) ) {
printf ( " Failed to load %s \n " , png_name ) ;
exit ( 1 ) ;
return false ;
}
dat . resize ( h ) ;
for ( int y = 0 ; y < h ; y + + ) {
dat [ y ] . resize ( w ) ;
memcpy ( & dat [ y ] [ 0 ] , img_data + 4 * y * w , 4 * w ) ;
}
free ( img_data ) ;
return true ;
}
void SavePNG ( const char * png_name ) {
// Save PNG
FILE * fil = fopen ( png_name , " wb " ) ;
png_structp png_ptr ;
png_infop info_ptr ;
png_ptr = png_create_write_struct ( PNG_LIBPNG_VER_STRING , NULL , NULL , NULL ) ;
CHECK ( png_ptr ) ;
info_ptr = png_create_info_struct ( png_ptr ) ;
CHECK ( info_ptr ) ;
png_init_io ( png_ptr , fil ) ;
//png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
png_set_IHDR ( png_ptr , info_ptr , dat [ 0 ] . size ( ) , dat . size ( ) , 8 , PNG_COLOR_TYPE_RGBA , PNG_INTERLACE_NONE , PNG_COMPRESSION_TYPE_DEFAULT , PNG_FILTER_TYPE_DEFAULT ) ;
png_write_info ( png_ptr , info_ptr ) ;
for ( int y = 0 ; y < ( int ) dat . size ( ) ; y + + ) {
png_write_row ( png_ptr , ( png_byte * ) & dat [ y ] [ 0 ] ) ;
}
png_write_end ( png_ptr , NULL ) ;
png_destroy_write_struct ( & png_ptr , & info_ptr ) ;
}
void SaveZIM ( const char * zim_name , int zim_format ) {
2015-09-16 01:13:15 -03:00
uint8_t * image_data = new uint8_t [ width ( ) * height ( ) * 4 ] ;
2012-10-26 10:45:51 +02:00
for ( int y = 0 ; y < height ( ) ; y + + ) {
memcpy ( image_data + y * width ( ) * 4 , & dat [ y ] [ 0 ] , width ( ) * 4 ) ;
}
: : SaveZIM ( zim_name , width ( ) , height ( ) , width ( ) * 4 , zim_format | ZIM_DITHER , image_data ) ;
}
} ;
template < class S , class T >
bool operator < ( const Image < S > & lhs , const Image < T > & rhs ) {
return lhs . dat . size ( ) * lhs . dat [ 0 ] . size ( ) > rhs . dat . size ( ) * rhs . dat [ 0 ] . size ( ) ;
}
struct Data {
// item ID
int id ;
// dimensions of its spot in the world
int sx , sy , ex , ey ;
// offset from the origin
float ox , oy ;
2013-04-21 21:53:46 +02:00
float voffset ; // to apply at the end
2012-10-26 10:45:51 +02:00
// distance to move the origin forward
float wx ;
int effect ;
2013-04-17 12:18:32 +02:00
int charNum ;
2012-10-26 10:45:51 +02:00
} ;
bool operator < ( const Data & lhs , const Data & rhs ) {
return lhs . id < rhs . id ; // should be unique
}
string out_prefix ;
int NextPowerOf2 ( int x ) {
int powof2 = 1 ;
// Double powof2 until >= val
while ( powof2 < x ) powof2 < < = 1 ;
return powof2 ;
}
struct Bucket {
2013-04-21 12:02:13 -07:00
vector < pair < Image < unsigned int > , Data > > items ;
void AddItem ( const Image < unsigned int > & img , const Data & dat ) {
items . push_back ( make_pair ( img , dat ) ) ;
}
vector < Data > Resolve ( int image_width , Image < unsigned int > & dest ) {
// Place all the little images - whatever they are.
// Uses greedy fill algorithm. Slow but works surprisingly well, CPUs are fast.
Image < unsigned char > masq ;
masq . resize ( image_width , 1 ) ;
dest . resize ( image_width , 1 ) ;
sort ( items . begin ( ) , items . end ( ) ) ;
for ( int i = 0 ; i < ( int ) items . size ( ) ; i + + ) {
2013-04-18 14:40:14 +02:00
if ( ( i + 1 ) % 200 = = 0 ) {
printf ( " Resolving (%i / %i) \n " , i , ( int ) items . size ( ) ) ;
}
2013-04-21 12:02:13 -07:00
int idx = items [ i ] . first . dat [ 0 ] . size ( ) ;
int idy = items [ i ] . first . dat . size ( ) ;
2013-04-18 14:40:14 +02:00
if ( idx > 1 & & idy > 1 ) {
2013-04-17 13:14:38 +02:00
CHECK ( idx < = image_width ) ;
2013-04-21 12:02:13 -07:00
for ( int ty = 0 ; ty < 2047 ; ty + + ) {
2013-04-17 13:14:38 +02:00
if ( ty + idy + 1 > ( int ) dest . dat . size ( ) ) {
2013-04-18 14:40:14 +02:00
masq . resize ( image_width , ty + idy + 16 ) ;
dest . resize ( image_width , ty + idy + 16 ) ;
2013-04-17 13:14:38 +02:00
}
// Brute force packing.
2013-04-17 16:31:41 +02:00
int sz = ( int ) items [ i ] . first . dat [ 0 ] . size ( ) ;
2013-04-21 12:02:13 -07:00
auto & masq_ty = masq . dat [ ty ] ;
auto & masq_idy = masq . dat [ ty + idy - 1 ] ;
for ( int tx = 0 ; tx < image_width - sz ; tx + + ) {
bool valid = ! ( masq_ty [ tx ] | | masq_idy [ tx ] | | masq_ty [ tx + idx - 1 ] | | masq_idy [ tx + idx - 1 ] ) ;
2013-04-17 13:14:38 +02:00
if ( valid ) {
for ( int ity = 0 ; ity < idy & & valid ; ity + + ) {
for ( int itx = 0 ; itx < idx & & valid ; itx + + ) {
if ( masq . dat [ ty + ity ] [ tx + itx ] ) {
2013-04-17 16:31:41 +02:00
goto skip ;
2013-04-17 13:14:38 +02:00
}
}
}
dest . copyfrom ( items [ i ] . first , tx , ty , items [ i ] . second . effect ) ;
masq . set ( tx , ty , tx + idx + 1 , ty + idy + 1 , 255 ) ;
items [ i ] . second . sx = tx ;
items [ i ] . second . sy = ty ;
items [ i ] . second . ex = tx + idx ;
items [ i ] . second . ey = ty + idy ;
// printf("Placed %d at %dx%d-%dx%d\n", items[i].second.id, tx, ty, tx + idx, ty + idy);
2013-04-21 12:02:13 -07:00
goto found ;
2013-04-17 13:14:38 +02:00
}
2013-04-17 16:31:41 +02:00
skip :
;
2013-04-17 13:14:38 +02:00
}
}
2013-04-21 12:02:13 -07:00
found :
;
}
}
2012-10-26 10:45:51 +02:00
2013-04-18 14:40:14 +02:00
if ( ( int ) dest . dat . size ( ) > image_width * 2 ) {
2012-10-26 10:45:51 +02:00
printf ( " PACKING FAIL : height=%i " , ( int ) dest . dat . size ( ) ) ;
exit ( 1 ) ;
}
dest . resize ( image_width , NextPowerOf2 ( dest . dat . size ( ) ) ) ;
// Output the glyph data.
vector < Data > dats ;
for ( int i = 0 ; i < ( int ) items . size ( ) ; i + + )
dats . push_back ( items [ i ] . second ) ;
return dats ;
}
} ;
const int supersample = 16 ;
const int distmult = 64 * 3 ; // this is "one pixel in the final version equals 64 difference". reduce this number to increase the "blur" radius, increase it to make things "sharper"
const int maxsearch = ( 128 * supersample + distmult - 1 ) / distmult ;
struct Closest {
FT_Bitmap bmp ;
Closest ( FT_Bitmap bmp ) : bmp ( bmp ) { }
float find_closest ( int x , int y , char search ) {
int best = 1 < < 30 ;
for ( int i = 1 ; i < = maxsearch ; i + + ) {
if ( i * i > = best )
break ;
for ( int f = - i ; f < i ; f + + ) {
int dist = i * i + f * f ;
if ( dist > = best ) continue ;
if ( safe_access ( x + i , y + f ) = = search | | safe_access ( x - f , y + i ) = = search | | safe_access ( x - i , y - f ) = = search | | safe_access ( x + f , y - i ) = = search )
best = dist ;
}
}
return sqrt ( ( float ) best ) ;
}
char safe_access ( int x , int y ) {
if ( x < 0 | | y < 0 | | x > = bmp . width | | y > = bmp . rows )
return 0 ;
return bmp . buffer [ x + y * bmp . width ] ;
}
2013-04-21 11:51:58 -07:00
} ;
2012-10-26 10:45:51 +02:00
2013-04-21 11:51:58 -07:00
typedef vector < FT_Face > FT_Face_List ;
inline vector < CharRange > merge ( const vector < CharRange > & a , const vector < CharRange > & b ) {
vector < CharRange > result = a ;
for ( size_t i = 0 , in = b . size ( ) ; i < in ; + + i ) {
bool insert = true ;
for ( size_t j = 0 , jn = a . size ( ) ; j < jn ; + + j ) {
// Should never overlap, so same start is always a duplicate.
if ( b [ i ] . start = = a [ j ] . start ) {
insert = false ;
break ;
}
}
2012-10-26 10:45:51 +02:00
2013-04-21 11:51:58 -07:00
if ( insert ) {
result . push_back ( b [ i ] ) ;
}
}
return result ;
}
2012-10-26 10:45:51 +02:00
2016-01-09 16:49:20 -03:00
void RasterizeFonts ( const FontReferenceList & fontRefs , vector < CharRange > & ranges , float * metrics_height , Bucket * bucket ) {
2013-04-21 11:51:58 -07:00
FT_Library freetype ;
CHECK ( FT_Init_FreeType ( & freetype ) = = 0 ) ;
2012-10-26 10:45:51 +02:00
2013-04-21 11:51:58 -07:00
vector < FT_Face > fonts ;
fonts . resize ( fontRefs . size ( ) ) ;
2012-10-26 10:45:51 +02:00
2013-04-21 11:51:58 -07:00
// The ranges may overlap, so build a list of fonts per range.
map < int , FT_Face_List > fontsByRange ;
// TODO: Better way than average?
float totalHeight = 0.0f ;
for ( size_t i = 0 , n = fontRefs . size ( ) ; i < n ; + + i ) {
FT_Face & font = fonts [ i ] ;
2014-02-05 20:03:28 +01:00
if ( FT_New_Face ( freetype , fontRefs [ i ] . file_ . c_str ( ) , 0 , & font ) ! = 0 ) {
2014-07-23 12:50:03 +02:00
printf ( " Failed to load font file %s \n " , fontRefs [ i ] . file_ . c_str ( ) ) ;
printf ( " bailing " ) ;
exit ( 0 ) ;
2014-02-05 20:03:28 +01:00
}
2013-04-21 11:51:58 -07:00
printf ( " TTF info: %d glyphs, %08x flags, %d units, %d strikes \n " , ( int ) font - > num_glyphs , ( int ) font - > face_flags , ( int ) font - > units_per_EM , ( int ) font - > num_fixed_sizes ) ;
CHECK ( FT_Set_Pixel_Sizes ( font , 0 , fontRefs [ i ] . size_ * supersample ) = = 0 ) ;
ranges = merge ( ranges , fontRefs [ i ] . ranges_ ) ;
for ( size_t r = 0 , rn = fontRefs [ i ] . ranges_ . size ( ) ; r < rn ; + + r ) {
const CharRange & range = fontRefs [ i ] . ranges_ [ r ] ;
fontsByRange [ range . start ] . push_back ( fonts [ i ] ) ;
}
2013-04-17 12:18:32 +02:00
2013-04-21 11:51:58 -07:00
totalHeight + = font - > size - > metrics . height ;
}
2016-02-10 11:47:59 +01:00
// Wait what - how does this make sense?
2013-04-21 11:51:58 -07:00
* metrics_height = totalHeight / ( float ) fontRefs . size ( ) ;
2013-04-17 12:18:32 +02:00
2013-04-21 11:51:58 -07:00
// Convert all characters to bitmaps.
for ( size_t r = 0 , rn = ranges . size ( ) ; r < rn ; r + + ) {
FT_Face_List & tryFonts = fontsByRange [ ranges [ r ] . start ] ;
2013-04-17 13:14:38 +02:00
ranges [ r ] . start_index = global_id ;
for ( int kar = ranges [ r ] . start ; kar < ranges [ r ] . end ; kar + + ) {
2013-04-18 14:40:14 +02:00
bool filtered = false ;
if ( ranges [ r ] . filter . size ( ) ) {
if ( ranges [ r ] . filter . find ( ( u16 ) kar ) = = ranges [ r ] . filter . end ( ) )
filtered = true ;
}
2013-04-21 11:51:58 -07:00
FT_Face font ;
bool foundMatch = false ;
2013-04-21 21:53:46 +02:00
float vertOffset = 0 ;
2013-04-21 11:51:58 -07:00
for ( size_t i = 0 , n = tryFonts . size ( ) ; i < n ; + + i ) {
font = tryFonts [ i ] ;
2013-04-21 21:53:46 +02:00
vertOffset = fontRefs [ i ] . vertOffset_ ;
2013-04-21 11:51:58 -07:00
if ( FT_Get_Char_Index ( font , kar ) ! = 0 ) {
foundMatch = true ;
break ;
}
}
2017-03-18 16:32:41 +01:00
//if (!foundMatch)
// fprintf(stderr, "WARNING: No font contains character %x.\n", kar);
2013-04-21 11:51:58 -07:00
2013-04-17 13:14:38 +02:00
Image < unsigned int > img ;
2013-04-18 14:40:14 +02:00
if ( filtered | | 0 ! = FT_Load_Char ( font , kar , FT_LOAD_RENDER | FT_LOAD_MONOCHROME ) ) {
2013-04-17 16:31:41 +02:00
img . resize ( 1 , 1 ) ;
2013-04-17 13:14:38 +02:00
Data dat ;
dat . id = global_id + + ;
dat . sx = 0 ;
dat . sy = 0 ;
dat . ex = 0 ;
dat . ey = 0 ;
dat . ox = 0 ;
dat . oy = 0 ;
dat . wx = 0 ;
2016-02-10 16:48:25 +01:00
dat . voffset = 0 ;
2013-04-17 13:14:38 +02:00
dat . charNum = kar ;
dat . effect = FX_RED_TO_ALPHA_SOLID_WHITE ;
bucket - > AddItem ( img , dat ) ;
continue ;
}
// printf("%dx%d %p\n", font->glyph->bitmap.width, font->glyph->bitmap.rows, font->glyph->bitmap.buffer);
const int bord = ( 128 + distmult - 1 ) / distmult + 1 ;
if ( font - > glyph - > bitmap . buffer ) {
FT_Bitmap tempbitmap ;
FT_Bitmap_New ( & tempbitmap ) ;
FT_Bitmap_Convert ( freetype , & font - > glyph - > bitmap , & tempbitmap , 1 ) ;
Closest closest ( tempbitmap ) ;
// No resampling, just sets the size of the image.
img . resize ( ( tempbitmap . width + supersample - 1 ) / supersample + bord * 2 , ( tempbitmap . rows + supersample - 1 ) / supersample + bord * 2 ) ;
int lmx = img . dat [ 0 ] . size ( ) ;
int lmy = img . dat . size ( ) ;
// AA by finding distance to character. Probably a fairly decent approximation but why not do it right?
for ( int y = 0 ; y < lmy ; y + + ) {
int cty = ( y - bord ) * supersample + supersample / 2 ;
for ( int x = 0 ; x < lmx ; x + + ) {
int ctx = ( x - bord ) * supersample + supersample / 2 ;
float dist ;
if ( closest . safe_access ( ctx , cty ) ) {
dist = closest . find_closest ( ctx , cty , 0 ) ;
} else {
dist = - closest . find_closest ( ctx , cty , 1 ) ;
}
dist = dist / supersample * distmult + 127.5 ;
dist = floor ( dist + 0.5 ) ;
if ( dist < 0 ) dist = 0 ;
if ( dist > 255 ) dist = 255 ;
// Only set the red channel. We process when adding the image.
img . dat [ y ] [ x ] = ( unsigned char ) dist ;
}
}
FT_Bitmap_Done ( freetype , & tempbitmap ) ;
} else {
img . resize ( 1 , 1 ) ;
}
2012-10-26 10:45:51 +02:00
Data dat ;
dat . id = global_id + + ;
dat . sx = 0 ;
dat . sy = 0 ;
2013-04-17 13:14:38 +02:00
dat . ex = img . dat [ 0 ] . size ( ) ;
dat . ey = img . dat . size ( ) ;
dat . ox = ( float ) font - > glyph - > metrics . horiBearingX / 64 / supersample - bord ;
dat . oy = - ( float ) font - > glyph - > metrics . horiBearingY / 64 / supersample - bord ;
2013-04-21 21:53:46 +02:00
dat . voffset = vertOffset ;
2013-04-17 13:14:38 +02:00
dat . wx = ( float ) font - > glyph - > metrics . horiAdvance / 64 / supersample ;
2013-04-17 12:18:32 +02:00
dat . charNum = kar ;
2013-04-17 13:14:38 +02:00
2012-10-26 10:45:51 +02:00
dat . effect = FX_RED_TO_ALPHA_SOLID_WHITE ;
bucket - > AddItem ( img , dat ) ;
}
2013-04-21 11:51:58 -07:00
}
2012-10-26 10:45:51 +02:00
2013-04-21 11:51:58 -07:00
for ( size_t i = 0 , n = fonts . size ( ) ; i < n ; + + i ) {
FT_Done_Face ( fonts [ i ] ) ;
}
FT_Done_FreeType ( freetype ) ;
2012-10-26 10:45:51 +02:00
}
bool LoadImage ( const char * imagefile , Effect effect , Bucket * bucket ) {
Image < unsigned int > img ;
bool success = false ;
if ( ! strcmp ( imagefile , " white.png " ) ) {
img . dat . resize ( 16 ) ;
for ( int i = 0 ; i < 16 ; i + + ) {
img . dat [ i ] . resize ( 16 ) ;
for ( int j = 0 ; j < 16 ; j + + ) {
img . dat [ i ] [ j ] = 0xFFFFFFFF ;
}
}
success = true ;
} else {
success = img . LoadPNG ( imagefile ) ;
2012-10-26 11:18:19 +02:00
// printf("loaded image: %ix%i\n", (int)img.dat[0].size(), (int)img.dat.size());
2012-10-26 10:45:51 +02:00
}
if ( ! success ) {
return false ;
}
Data dat ;
memset ( & dat , 0 , sizeof ( dat ) ) ;
dat . id = global_id + + ;
dat . sx = 0 ;
dat . sy = 0 ;
dat . ex = img . dat [ 0 ] . size ( ) ;
dat . ey = img . dat . size ( ) ;
dat . effect = effect ;
bucket - > AddItem ( img , dat ) ;
return true ;
}
// Use the result array, and recorded data, to generate C++ tables for everything.
struct FontDesc {
string name ;
int first_char_id ;
int last_char_id ;
float ascend ;
float descend ;
float height ;
float metrics_height ;
2013-04-18 14:40:14 +02:00
std : : vector < CharRange > ranges ;
2013-04-17 12:18:32 +02:00
2013-04-17 13:14:38 +02:00
FontDesc ( )
{
2013-04-17 12:18:32 +02:00
}
2012-10-26 10:45:51 +02:00
void ComputeHeight ( const vector < Data > & results , float distmult ) {
ascend = 0 ;
descend = 0 ;
2013-04-17 16:31:41 +02:00
for ( size_t r = 0 ; r < ranges . size ( ) ; r + + ) {
for ( int i = ranges [ r ] . start ; i < ranges [ r ] . end ; i + + ) {
int idx = i - ranges [ r ] . start + ranges [ r ] . start_index ;
ascend = max ( ascend , - results [ idx ] . oy ) ;
descend = max ( descend , results [ idx ] . ey - results [ idx ] . sy + results [ idx ] . oy ) ;
}
}
2012-10-26 10:45:51 +02:00
height = metrics_height / 64.0 / supersample ;
}
void OutputSelf ( FILE * fil , float tw , float th , const vector < Data > & results ) {
2013-04-17 12:18:32 +02:00
// Dump results as chardata.
fprintf ( fil , " const AtlasChar font_%s_chardata[] = { \n " , name . c_str ( ) ) ;
2013-04-17 13:14:38 +02:00
for ( size_t r = 0 ; r < ranges . size ( ) ; r + + ) {
fprintf ( fil , " // RANGE: 0x%x - 0x%x, start 0x%x \n " , ranges [ r ] . start , ranges [ r ] . end , ranges [ r ] . start_index ) ;
for ( int i = ranges [ r ] . start ; i < ranges [ r ] . end ; i + + ) {
int idx = i - ranges [ r ] . start + ranges [ r ] . start_index ;
2013-04-18 14:40:14 +02:00
fprintf ( fil , " {%ff, %ff, %ff, %ff, %1.4ff, %1.4ff, %1.4ff, %i, %i}, // %04x \n " ,
2013-04-17 13:14:38 +02:00
/*results[i].id, */
results [ idx ] . sx / tw ,
results [ idx ] . sy / th ,
results [ idx ] . ex / tw ,
results [ idx ] . ey / th ,
results [ idx ] . ox ,
2013-04-21 21:53:46 +02:00
results [ idx ] . oy + results [ idx ] . voffset ,
2013-04-17 13:14:38 +02:00
results [ idx ] . wx ,
results [ idx ] . ex - results [ idx ] . sx , results [ idx ] . ey - results [ idx ] . sy ,
results [ idx ] . charNum ) ;
}
2013-04-17 12:18:32 +02:00
}
fprintf ( fil , " }; \n " ) ;
fprintf ( fil , " const AtlasCharRange font_%s_ranges[] = { \n " , name . c_str ( ) ) ;
// Write range information.
int start_index = 0 ;
2013-04-17 13:14:38 +02:00
for ( size_t r = 0 ; r < ranges . size ( ) ; r + + ) {
2013-04-17 12:18:32 +02:00
int first_char_id = ranges [ r ] . start ;
int last_char_id = ranges [ r ] . end ;
2013-04-17 21:45:31 +02:00
fprintf ( fil , " { %i, %i, %i }, \n " , first_char_id , last_char_id , start_index ) ;
2013-04-17 12:18:32 +02:00
start_index + = last_char_id - first_char_id ;
}
fprintf ( fil , " }; \n " ) ;
2012-10-26 10:45:51 +02:00
fprintf ( fil , " const AtlasFont font_%s = { \n " , name . c_str ( ) ) ;
fprintf ( fil , " %ff, // padding \n " , height - ascend - descend ) ;
fprintf ( fil , " %ff, // height \n " , ascend + descend ) ;
fprintf ( fil , " %ff, // ascend \n " , ascend ) ;
fprintf ( fil , " %ff, // distslope \n " , distmult / 256.0 ) ;
2013-04-17 12:18:32 +02:00
fprintf ( fil , " font_%s_chardata, \n " , name . c_str ( ) ) ;
fprintf ( fil , " font_%s_ranges, \n " , name . c_str ( ) ) ;
fprintf ( fil , " %i, \n " , ( int ) ranges . size ( ) ) ;
2012-10-31 10:14:59 +01:00
fprintf ( fil , " \" %s \" , // name \n " , name . c_str ( ) ) ;
2012-10-26 10:45:51 +02:00
fprintf ( fil , " }; \n " ) ;
}
void OutputIndex ( FILE * fil ) {
fprintf ( fil , " &font_%s, \n " , name . c_str ( ) ) ;
}
void OutputHeader ( FILE * fil , int index ) {
fprintf ( fil , " #define %s %i \n " , name . c_str ( ) , index ) ;
}
} ;
struct ImageDesc {
string name ;
Effect effect ;
int result_index ;
void OutputSelf ( FILE * fil , float tw , float th , const vector < Data > & results ) {
int i = result_index ;
float toffx = 0.5f / tw ;
float toffy = 0.5f / th ;
2012-10-31 10:14:59 +01:00
fprintf ( fil , " {%ff, %ff, %ff, %ff, %d, %d, \" %s \" }, \n " ,
2012-10-26 10:45:51 +02:00
results [ i ] . sx / tw + toffx ,
results [ i ] . sy / th + toffy ,
results [ i ] . ex / tw - toffx ,
results [ i ] . ey / th - toffy ,
results [ i ] . ex - results [ i ] . sx ,
2012-10-31 10:14:59 +01:00
results [ i ] . ey - results [ i ] . sy ,
name . c_str ( ) ) ;
2012-10-26 10:45:51 +02:00
}
void OutputHeader ( FILE * fil , int index ) {
fprintf ( fil , " #define %s %i \n " , name . c_str ( ) , index ) ;
}
} ;
2013-04-18 14:40:14 +02:00
CharRange range ( int start , int end , const std : : set < u16 > & filter ) {
CharRange r ;
r . start = start ;
r . end = end + 1 ;
r . start_index = 0 ;
r . filter = filter ;
2013-04-17 13:14:38 +02:00
return r ;
}
2013-04-18 14:40:14 +02:00
CharRange range ( int start , int end ) {
CharRange r ;
r . start = start ;
r . end = end + 1 ;
r . start_index = 0 ;
return r ;
}
inline bool operator < ( const CharRange & a , const CharRange & b ) {
// These ranges should never overlap so this should be enough.
return a . start < b . start ;
}
2013-04-19 23:54:38 +02:00
2013-04-21 11:51:58 -07:00
void LearnFile ( const char * filename , const char * desc , std : : set < u16 > & chars , uint32_t lowerLimit , uint32_t upperLimit ) {
2013-04-20 17:41:17 +02:00
FILE * f = fopen ( filename , " rb " ) ;
2013-04-19 23:54:38 +02:00
if ( f ) {
fseek ( f , 0 , SEEK_END ) ;
size_t sz = ftell ( f ) ;
fseek ( f , 0 , SEEK_SET ) ;
char * data = new char [ sz + 1 ] ;
fread ( data , 1 , sz , f ) ;
fclose ( f ) ;
data [ sz ] = 0 ;
UTF8 utf ( data ) ;
int learnCount = 0 ;
while ( ! utf . end ( ) ) {
uint32_t c = utf . next ( ) ;
2013-04-20 17:41:17 +02:00
if ( c > = lowerLimit & & c < = upperLimit ) {
if ( chars . find ( c ) = = chars . end ( ) ) {
2013-04-19 23:54:38 +02:00
learnCount + + ;
2013-04-20 17:41:17 +02:00
chars . insert ( c ) ;
2013-04-19 23:54:38 +02:00
}
}
}
delete [ ] data ;
2013-04-20 17:41:17 +02:00
printf ( " %i %s characters learned. \n " , learnCount , desc ) ;
2013-04-19 23:54:38 +02:00
}
2013-04-20 17:41:17 +02:00
}
void GetLocales ( const char * locales , std : : vector < CharRange > & ranges )
{
std : : set < u16 > kanji ;
std : : set < u16 > hangul1 , hangul2 , hangul3 ;
for ( int i = 0 ; i < sizeof ( kanjiFilter ) / sizeof ( kanjiFilter [ 0 ] ) ; i + = 2 )
{
2013-05-02 01:08:07 +09:00
// Kanji filtering.
2013-05-02 00:22:59 +09:00
if ( ( kanjiFilter [ i + 1 ] & USE_KANJI ) > 0 ) {
2013-04-20 17:41:17 +02:00
kanji . insert ( kanjiFilter [ i ] ) ;
}
}
// Also, load chinese.txt if available.
LearnFile ( " chinese.txt " , " Chinese " , kanji , 0x3400 , 0xFFFF ) ;
LearnFile ( " korean.txt " , " Korean " , hangul1 , 0x1100 , 0x11FF ) ;
LearnFile ( " korean.txt " , " Korean " , hangul2 , 0x3130 , 0x318F ) ;
LearnFile ( " korean.txt " , " Korean " , hangul3 , 0xAC00 , 0xD7A3 ) ;
2013-04-18 14:40:14 +02:00
// The end point of a range is now inclusive!
2013-04-17 13:14:38 +02:00
for ( size_t i = 0 ; i < strlen ( locales ) ; i + + ) {
switch ( locales [ i ] ) {
case ' U ' : // US ASCII
2013-04-18 14:40:14 +02:00
ranges . push_back ( range ( 32 , 127 ) ) ;
2013-04-17 13:14:38 +02:00
break ;
case ' W ' : // Latin-1 extras 1
2013-04-18 14:40:14 +02:00
ranges . push_back ( range ( 0x80 , 0x80 ) ) ; // euro sign
2013-04-17 13:14:38 +02:00
ranges . push_back ( range ( 0xA2 , 0xFF ) ) ; // 80 - A0 appears to contain nothing interesting
2013-05-05 06:25:00 +09:00
ranges . push_back ( range ( 0x2121 , 0x2122 ) ) ; // TEL symbol and trademark symbol
2013-04-17 13:14:38 +02:00
break ;
2013-04-18 22:09:26 +02:00
case ' E ' : // Latin-1 Extended A (needed for Hungarian etc)
ranges . push_back ( range ( 0x100 , 0x17F ) ) ;
break ;
2013-04-19 23:54:38 +02:00
case ' e ' : // Latin-1 Extended B (for some African and latinized asian languages?)
ranges . push_back ( range ( 0x180 , 0x250 ) ) ;
break ;
2013-04-17 15:04:40 +02:00
case ' k ' : // Katakana
ranges . push_back ( range ( 0x30A0 , 0x30FF ) ) ;
2013-04-18 14:40:14 +02:00
ranges . push_back ( range ( 0x31F0 , 0x31FF ) ) ;
2013-04-18 22:09:26 +02:00
ranges . push_back ( range ( 0xFF00 , 0xFFEF ) ) ; // half-width ascii
2013-04-17 15:04:40 +02:00
break ;
case ' h ' : // Hiragana
ranges . push_back ( range ( 0x3041 , 0x3097 ) ) ;
ranges . push_back ( range ( 0x3099 , 0x309F ) ) ;
break ;
2013-05-02 00:22:59 +09:00
case ' s ' : // ShiftJIS symbols
ranges . push_back ( range ( 0x2010 , 0x2312 ) ) ; // General Punctuation, Letterlike Symbols, Arrows,
// Mathematical Operators, Miscellaneous Technical
ranges . push_back ( range ( 0x2500 , 0x254B ) ) ; // Box drawing
ranges . push_back ( range ( 0x25A0 , 0x266F ) ) ; // Geometric Shapes, Miscellaneous Symbols
2013-05-05 06:25:00 +09:00
ranges . push_back ( range ( 0x3231 , 0x3231 ) ) ; // Co,.Ltd. symbol
ranges . push_back ( range ( 0x2116 , 0x2116 ) ) ; // "No." symbol
ranges . push_back ( range ( 0x33CD , 0x33CD ) ) ; // "K.K." symbol
2013-08-18 18:39:32 +10:00
break ;
2013-04-17 15:04:40 +02:00
case ' H ' : // Hebrew
ranges . push_back ( range ( 0x0590 , 0x05FF ) ) ;
break ;
case ' G ' : // Greek
ranges . push_back ( range ( 0x0370 , 0x03FF ) ) ;
break ;
2013-04-18 14:40:14 +02:00
case ' R ' : // Russian
ranges . push_back ( range ( 0x0400 , 0x04FF ) ) ;
break ;
2013-04-19 23:54:38 +02:00
case ' c ' : // All Kanji, filtered though!
2013-04-20 00:00:51 +02:00
ranges . push_back ( range ( 0x3000 , 0x303f ) ) ; // Ideographic symbols
2013-04-20 17:41:17 +02:00
ranges . push_back ( range ( 0x4E00 , 0x9FFF , kanji ) ) ;
// ranges.push_back(range(0xFB00, 0xFAFF, kanji));
2013-04-17 15:04:40 +02:00
break ;
2013-04-18 22:09:26 +02:00
case ' T ' : // Thai
ranges . push_back ( range ( 0x0E00 , 0x0E5B ) ) ;
break ;
2013-04-20 17:41:17 +02:00
case ' K ' : // Korean (hangul)
ranges . push_back ( range ( 0xAC00 , 0xD7A3 , hangul3 ) ) ;
break ;
2013-11-20 00:05:31 +01:00
case ' V ' : // Vietnamese (need 'e' too)
ranges . push_back ( range ( 0x1EA0 , 0x1EF9 ) ) ;
break ;
2013-04-17 13:14:38 +02:00
}
}
2013-04-18 14:40:14 +02:00
ranges . push_back ( range ( 0xFFFD , 0xFFFD ) ) ;
std : : sort ( ranges . begin ( ) , ranges . end ( ) ) ;
2013-04-17 13:14:38 +02:00
}
2012-10-26 10:45:51 +02:00
int main ( int argc , char * * argv ) {
// initProgram(&argc, const_cast<const char ***>(&argv));
// /usr/share/fonts/truetype/msttcorefonts/Arial_Black.ttf
// /usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-R.ttf
2014-07-15 17:24:15 +02:00
if ( argc < 3 ) {
FLOG ( " Not enough arguments " ) ;
return 1 ;
}
2013-11-17 15:34:51 +10:00
CHECK ( argc > = 3 ) ;
2012-10-26 10:45:51 +02:00
if ( argc > 3 )
{
2013-11-17 15:34:51 +10:00
if ( ! strcmp ( argv [ 3 ] , " etc1 " ) ) {
printf ( " ETC1 enabled! \n " ) ;
etc1 = true ;
} else if ( ! strcmp ( argv [ 3 ] , " 8888 " ) ) {
highcolor = true ;
printf ( " RGBA8888 enabled! \n " ) ;
}
2012-10-26 10:45:51 +02:00
}
printf ( " Reading script %s \n " , argv [ 1 ] ) ;
const char * atlas_name = argv [ 2 ] ;
string image_name = string ( atlas_name ) + " _atlas.zim " ;
out_prefix = argv [ 2 ] ;
2013-04-21 11:51:58 -07:00
map < string , FontReferenceList > fontRefs ;
2012-10-26 10:45:51 +02:00
vector < FontDesc > fonts ;
vector < ImageDesc > images ;
Bucket bucket ;
char line [ 512 ] ;
FILE * script = fopen ( argv [ 1 ] , " r " ) ;
if ( ! fgets ( line , 512 , script ) ) {
printf ( " Error fgets-ing \n " ) ;
}
int image_width ;
sscanf ( line , " %i " , & image_width ) ;
printf ( " Texture width: %i \n " , image_width ) ;
2013-04-21 11:51:58 -07:00
while ( ! feof ( script ) ) {
if ( ! fgets ( line , 511 , script ) ) break ;
if ( ! strlen ( line ) ) break ;
2014-07-15 17:42:21 +02:00
if ( line [ 0 ] = = ' # ' ) continue ;
2013-04-21 11:51:58 -07:00
char * rest = strchr ( line , ' ' ) ;
if ( rest ) {
2012-10-26 10:45:51 +02:00
* rest = 0 ;
rest + + ;
}
2013-04-21 11:51:58 -07:00
char * word = line ;
if ( ! strcmp ( word , " font " ) ) {
// Font!
char fontname [ 256 ] ;
char fontfile [ 256 ] ;
2013-04-17 13:14:38 +02:00
char locales [ 256 ] ;
2013-04-21 11:51:58 -07:00
int pixheight ;
2013-05-05 06:25:00 +09:00
float vertOffset = 0 ;
2016-12-02 22:46:52 -03:00
sscanf ( rest , " %255s %255s %255s %i %f " , fontname , fontfile , locales , & pixheight , & vertOffset ) ;
2013-04-21 11:51:58 -07:00
printf ( " Font: %s (%s) in size %i. Locales: %s \n " , fontname , fontfile , pixheight , locales ) ;
2012-10-26 10:45:51 +02:00
2013-04-18 14:40:14 +02:00
std : : vector < CharRange > ranges ;
2013-04-17 13:14:38 +02:00
GetLocales ( locales , ranges ) ;
printf ( " locales fetched. \n " ) ;
2012-10-26 10:45:51 +02:00
2013-04-21 21:53:46 +02:00
FontReference fnt ( fontname , fontfile , ranges , pixheight , vertOffset ) ;
2013-04-21 11:51:58 -07:00
fontRefs [ fontname ] . push_back ( fnt ) ;
2012-10-26 10:45:51 +02:00
} else if ( ! strcmp ( word , " image " ) ) {
char imagename [ 256 ] ;
char imagefile [ 256 ] ;
char effectname [ 256 ] ;
2016-12-02 22:46:52 -03:00
sscanf ( rest , " %255s %255s %255s " , imagename , imagefile , effectname ) ;
2012-10-26 10:45:51 +02:00
Effect effect = GetEffect ( effectname ) ;
printf ( " Image %s with effect %s (%i) \n " , imagefile , effectname , ( int ) effect ) ;
ImageDesc desc ;
desc . name = imagename ;
desc . effect = effect ;
desc . result_index = ( int ) bucket . items . size ( ) ;
images . push_back ( desc ) ;
if ( ! LoadImage ( imagefile , effect , & bucket ) ) {
fprintf ( stderr , " Failed to load image %s \n " , imagefile ) ;
}
} else {
fprintf ( stderr , " Warning: Failed to parse line starting with %s \n " , line ) ;
}
2013-04-21 11:51:58 -07:00
}
fclose ( script ) ;
// Script fully read, now rasterize the fonts.
for ( auto it = fontRefs . begin ( ) , end = fontRefs . end ( ) ; it ! = end ; + + it ) {
FontDesc fnt ;
fnt . first_char_id = ( int ) bucket . items . size ( ) ;
vector < CharRange > finalRanges ;
float metrics_height ;
RasterizeFonts ( it - > second , finalRanges , & metrics_height , & bucket ) ;
printf ( " font rasterized. \n " ) ;
fnt . ranges = finalRanges ;
fnt . name = it - > first ;
fnt . metrics_height = metrics_height ;
fonts . push_back ( fnt ) ;
}
2012-10-26 10:45:51 +02:00
// Script read, all subimages have been generated.
// Place the subimages onto the main texture. Also writes to png.
Image < unsigned int > dest ;
// Place things on the bitmap.
printf ( " Resolving... \n " ) ;
vector < Data > results = bucket . Resolve ( image_width , dest ) ;
2013-11-17 15:34:51 +10:00
if ( etc1 ) {
printf ( " Writing .ZIM %ix%i ETC1... \n " , dest . width ( ) , dest . height ( ) ) ;
dest . SaveZIM ( image_name . c_str ( ) , ZIM_ETC1 | ZIM_ZLIB_COMPRESSED ) ;
} else if ( highcolor ) {
2012-10-26 10:45:51 +02:00
printf ( " Writing .ZIM %ix%i RGBA8888... \n " , dest . width ( ) , dest . height ( ) ) ;
2013-04-18 14:40:14 +02:00
dest . SaveZIM ( image_name . c_str ( ) , ZIM_RGBA8888 | ZIM_ZLIB_COMPRESSED ) ;
2012-10-26 10:45:51 +02:00
} else {
printf ( " Writing .ZIM %ix%i RGBA4444... \n " , dest . width ( ) , dest . height ( ) ) ;
2013-04-18 14:40:14 +02:00
dest . SaveZIM ( image_name . c_str ( ) , ZIM_RGBA4444 | ZIM_ZLIB_COMPRESSED ) ;
2012-10-26 10:45:51 +02:00
}
// Also save PNG for debugging.
printf ( " Writing .PNG %s \n " , ( image_name + " .png " ) . c_str ( ) ) ;
dest . SavePNG ( ( image_name + " .png " ) . c_str ( ) ) ;
printf ( " Done. Outputting source files %s_atlas.cpp/h. \n " , out_prefix . c_str ( ) ) ;
// Sort items by ID.
sort ( results . begin ( ) , results . end ( ) ) ;
2012-10-31 10:14:59 +01:00
FILE * cpp_file = fopen ( ( out_prefix + " _atlas.cpp " ) . c_str ( ) , " wb " ) ;
fprintf ( cpp_file , " // C++ generated by atlastool from %s (hrydgard@gmail.com) \n \n " , argv [ 1 ] ) ;
fprintf ( cpp_file , " #include \" %s \" \n \n " , ( out_prefix + " _atlas.h " ) . c_str ( ) ) ;
2012-10-26 10:45:51 +02:00
for ( int i = 0 ; i < ( int ) fonts . size ( ) ; i + + ) {
FontDesc & xfont = fonts [ i ] ;
xfont . ComputeHeight ( results , distmult ) ;
2012-10-31 10:14:59 +01:00
xfont . OutputSelf ( cpp_file , dest . width ( ) , dest . height ( ) , results ) ;
2012-10-26 10:45:51 +02:00
}
if ( fonts . size ( ) ) {
2012-10-31 10:14:59 +01:00
fprintf ( cpp_file , " const AtlasFont *%s_fonts[%i] = { \n " , atlas_name , ( int ) fonts . size ( ) ) ;
2012-10-26 10:45:51 +02:00
for ( int i = 0 ; i < ( int ) fonts . size ( ) ; i + + ) {
2012-10-31 10:14:59 +01:00
fonts [ i ] . OutputIndex ( cpp_file ) ;
2012-10-26 10:45:51 +02:00
}
2012-10-31 10:14:59 +01:00
fprintf ( cpp_file , " }; \n " ) ;
2012-10-26 10:45:51 +02:00
}
if ( images . size ( ) ) {
2012-10-31 10:14:59 +01:00
fprintf ( cpp_file , " const AtlasImage %s_images[%i] = { \n " , atlas_name , ( int ) images . size ( ) ) ;
2012-10-26 10:45:51 +02:00
for ( int i = 0 ; i < ( int ) images . size ( ) ; i + + ) {
2012-10-31 10:14:59 +01:00
images [ i ] . OutputSelf ( cpp_file , dest . width ( ) , dest . height ( ) , results ) ;
2012-10-26 10:45:51 +02:00
}
2012-10-31 10:14:59 +01:00
fprintf ( cpp_file , " }; \n " ) ;
2012-10-26 10:45:51 +02:00
}
2012-10-31 10:14:59 +01:00
fprintf ( cpp_file , " const Atlas %s_atlas = { \n " , atlas_name ) ;
fprintf ( cpp_file , " \" %s \" , \n " , image_name . c_str ( ) ) ;
2012-10-26 10:45:51 +02:00
if ( fonts . size ( ) ) {
2012-10-31 10:14:59 +01:00
fprintf ( cpp_file , " %s_fonts, %i, \n " , atlas_name , ( int ) fonts . size ( ) ) ;
2012-10-26 10:45:51 +02:00
} else {
2012-10-31 10:14:59 +01:00
fprintf ( cpp_file , " 0, 0, \n " ) ;
2012-10-26 10:45:51 +02:00
}
if ( images . size ( ) ) {
2012-10-31 10:14:59 +01:00
fprintf ( cpp_file , " %s_images, %i, \n " , atlas_name , ( int ) images . size ( ) ) ;
2012-10-26 10:45:51 +02:00
} else {
2012-10-31 10:14:59 +01:00
fprintf ( cpp_file , " 0, 0, \n " ) ;
2012-10-26 10:45:51 +02:00
}
2012-10-31 10:14:59 +01:00
fprintf ( cpp_file , " }; \n " ) ;
2012-10-26 10:45:51 +02:00
// Should output a list pointing to all the fonts as well.
2012-10-31 10:14:59 +01:00
fclose ( cpp_file ) ;
FILE * h_file = fopen ( ( out_prefix + " _atlas.h " ) . c_str ( ) , " wb " ) ;
fprintf ( h_file , " // Header generated by atlastool from %s (hrydgard@gmail.com) \n \n " , argv [ 1 ] ) ;
fprintf ( h_file , " #pragma once \n " ) ;
fprintf ( h_file , " #include \" gfx/texture_atlas.h \" \n \n " ) ;
2012-10-26 10:45:51 +02:00
if ( fonts . size ( ) ) {
2012-10-31 10:14:59 +01:00
fprintf ( h_file , " // FONTS_%s \n " , atlas_name ) ;
2012-10-26 10:45:51 +02:00
for ( int i = 0 ; i < ( int ) fonts . size ( ) ; i + + ) {
2012-10-31 10:14:59 +01:00
fonts [ i ] . OutputHeader ( h_file , i ) ;
2012-10-26 10:45:51 +02:00
}
2012-10-31 10:14:59 +01:00
fprintf ( h_file , " \n \n " ) ;
2012-10-26 10:45:51 +02:00
}
if ( images . size ( ) ) {
2012-10-31 10:14:59 +01:00
fprintf ( h_file , " // IMAGES_%s \n " , atlas_name ) ;
2012-10-26 10:45:51 +02:00
for ( int i = 0 ; i < ( int ) images . size ( ) ; i + + ) {
2012-10-31 10:14:59 +01:00
images [ i ] . OutputHeader ( h_file , i ) ;
2012-10-26 10:45:51 +02:00
}
2012-10-31 10:14:59 +01:00
fprintf ( h_file , " \n \n " ) ;
2012-10-26 10:45:51 +02:00
}
2012-10-31 10:14:59 +01:00
fprintf ( h_file , " extern const Atlas %s_atlas; \n " , atlas_name ) ;
fprintf ( h_file , " extern const AtlasImage %s_images[%i]; \n " , atlas_name , ( int ) images . size ( ) ) ;
fclose ( h_file ) ;
2012-10-26 10:45:51 +02:00
}