1
0
mirror of https://github.com/azure-rtos/guix.git synced 2025-02-04 07:13:17 +08:00
guix/guix_studio/gx_studio_font_util.cpp

1631 lines
47 KiB
C++

#include "studiox_includes.h"
#include "ft2build.h"
#include FT_FREETYPE_H
#include "gx_studio_font_util.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
typedef struct {
int ascent;
int descent;
int max_height;
} FONT_METRICS;
typedef struct {
int glyph;
int value;
} KERNING_INFO;
///////////////////////////////////////////////////////////////////////////////
int gx_studio_font_util_open(const char *font_file, GXS_FONT_LIBRARY_HANDLE **handle)
{
FT_Error error;
GXS_FONT_LIBRARY_HANDLE *font_handle;
if(font_file == NULL)
return(GXS_FONT_UTIL_INVALID_FILENAME_PTR);
font_handle = (GXS_FONT_LIBRARY_HANDLE*)malloc(sizeof(GXS_FONT_LIBRARY_HANDLE));
if(font_handle == 0)
return(GXS_FONT_UTIL_MALLOC_FAILURE);
memset(font_handle, 0, sizeof(GXS_FONT_LIBRARY_HANDLE));
error = FT_Init_FreeType(&(font_handle -> library)); /* initialize library */
if(error)
{
free(font_handle);
return(GXS_FONT_UTIL_FT_INIT_FREETYPE_ERROR);
}
error = FT_New_Face(font_handle -> library, font_file, 0, &(font_handle -> face) );/* create face object */
if(error)
{
FT_Done_FreeType( font_handle -> library );
free(font_handle);
return(GXS_FONT_UTIL_CANNOT_OPEN_FONT_FILE);
}
font_handle -> size = 0;
font_handle -> initialized = 1;
*handle = font_handle;
return(GXS_FONT_UTIL_SUCCESS);
}
///////////////////////////////////////////////////////////////////////////////
int gx_studio_font_util_get_glyph(GXS_FONT_LIBRARY_HANDLE *font_handle, int font_index, int pt, int aa, GXS_FONT_DATA *font_data)
{
FT_Glyph_Metrics *metrics;
FT_GlyphSlot slot;
FT_Error error;
int glyph_index;
if(font_handle -> initialized != 1)
return(GXS_FONT_UTIL_LIBRARY_UNINITIAZLIED);
/* set character size */
if(font_handle -> size != pt)
{
error = FT_Set_Char_Size(font_handle -> face, pt * 64, 0, 72, 0);
if(error)
{
return(GXS_FONT_UTIL_SET_CHAR_SIZE_ERROR);
}
font_handle -> size = pt;
}
slot = font_handle -> face -> glyph;
glyph_index = FT_Get_Char_Index(font_handle -> face, font_index);
error = FT_Load_Glyph(font_handle -> face, glyph_index, 0);
if(font_handle -> face -> glyph -> format != FT_GLYPH_FORMAT_BITMAP)
{
if(aa)
error = FT_Render_Glyph(font_handle -> face -> glyph, FT_RENDER_MODE_NORMAL);
else
error = FT_Render_Glyph(font_handle -> face -> glyph, FT_RENDER_MODE_MONO);
}
if (aa && slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
{
error = FT_Load_Glyph(font_handle->face, glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_RENDER);
}
metrics = &(font_handle -> face -> glyph -> metrics);
font_data -> width = slot -> bitmap.width;
font_data -> height = slot -> bitmap.rows;
font_data -> pitch = slot -> bitmap.pitch;
font_data -> gx_glyph_ascent = (short) ((metrics -> horiBearingY) >> 6);
font_data -> gx_glyph_descent = (short)((metrics->height - metrics->horiBearingY) >> 6);
font_data -> gx_glyph_advance = (unsigned char) ((metrics -> horiAdvance) >> 6);
font_data -> gx_glyph_leading = (char) ((metrics -> horiBearingX) >> 6);
font_data -> gx_glyph_data = slot -> bitmap.buffer;
return(GXS_FONT_UTIL_SUCCESS);
}
///////////////////////////////////////////////////////////////////////////////
int gx_studio_font_util_get_kerning_info(GXS_FONT_LIBRARY_HANDLE *font_handle, int font_index,
int first_glyph_index, int last_glyph_index, GX_KERNING_GLYPH *kerning_glyph, FontCharMap *map)
{
FT_Error error;
FT_Vector kerning_vector;
GX_UBYTE left_glyph_index;
GX_UBYTE right_glyph_index;
INT index;
CArray<KERNING_INFO> kerning_pairs;
KERNING_INFO kerning_info;
INT table_size;
GX_UBYTE *left_glyph_ptr;
INT first_glyph = first_glyph_index;
INT last_glyph = last_glyph_index;
if (font_handle->initialized != 1)
return(GXS_FONT_UTIL_LIBRARY_UNINITIAZLIED);
if (FT_HAS_KERNING(font_handle->face) == 0)
{
// The face object does not contain kerning data.
return(GXS_FONT_UTIL_SUCCESS);
}
if (!map)
{
return(GXS_FONT_UTIL_SUCCESS);
}
kerning_pairs.SetSize(0, 1);
if (first_glyph > 0xff)
{
return 0;
}
if (last_glyph > 0xff)
{
last_glyph = 0xff;
}
right_glyph_index = FT_Get_Char_Index(font_handle->face, font_index);
if (!right_glyph_index)
{
// Undefined character code.
return(GXS_FONT_UTIL_SUCCESS);
}
// Get kerning vector between two glyphs in the same face
for (index = first_glyph; index <= last_glyph; index++)
{
if (!map->Test(index))
{
continue;
}
left_glyph_index = FT_Get_Char_Index(font_handle->face, index);
if (!left_glyph_index)
{
continue;
}
memset(&kerning_vector, 0, sizeof(FT_Vector));
error = FT_Get_Kerning(font_handle->face, left_glyph_index, right_glyph_index, FT_KERNING_DEFAULT, &kerning_vector);
if ((error == 0) && (kerning_vector.x))
{
kerning_info.glyph = index;
kerning_info.value = (kerning_vector.x >> 6);
kerning_pairs.Add(kerning_info);
}
}
if (kerning_pairs.GetCount())
{
// Calculate size of needed buffer. 1 byte size + 2 bytes for each kerning pair.
table_size = 1 + kerning_pairs.GetCount() * (sizeof(GX_UBYTE) + sizeof(GX_CHAR));
// Allocate memory to load kerning table.
kerning_glyph->gx_kerning_table = new GX_UBYTE[table_size];
memset(const_cast<GX_UBYTE *>(kerning_glyph->gx_kerning_table), 0, table_size);
left_glyph_ptr = (GX_UBYTE *)(kerning_glyph->gx_kerning_table + 1);
if (kerning_glyph->gx_kerning_table)
{
*(const_cast<GX_UBYTE *>(kerning_glyph->gx_kerning_table)) = kerning_pairs.GetCount();
for (index = 0; index < kerning_pairs.GetCount(); index++)
{
kerning_info = kerning_pairs.GetAt(index);
*left_glyph_ptr++ = (GX_UBYTE)kerning_info.glyph;
*left_glyph_ptr++ = (GX_UBYTE)kerning_info.value;
}
}
}
return(GXS_FONT_UTIL_SUCCESS);
}
///////////////////////////////////////////////////////////////////////////////
void gx_studio_font_util_close(GXS_FONT_LIBRARY_HANDLE *font_handle)
{
if(!font_handle)
return;
if(font_handle -> initialized == 1)
{
if(font_handle -> face)
FT_Done_Face(font_handle -> face);
if(font_handle -> library)
FT_Done_FreeType(font_handle -> library);
}
free(font_handle);
return;
}
///////////////////////////////////////////////////////////////////////////////
INT GetRowPitch(INT width, INT bits_per_pix)
{
// Calcualte data size of glyph map
INT pitch = width;
switch (bits_per_pix)
{
case 1:
pitch += 7;
pitch /= 8;
break;
case 4:
pitch ++;
pitch /= 2;
break;
case 8:
break;
}
return pitch;
}
///////////////////////////////////////////////////////////////////////////////
INT GetFontBits(INT font_format)
{
int font_bits = 8;
switch (font_format & GX_FONT_FORMAT_BPP_MASK)
{
case GX_FONT_FORMAT_1BPP:
font_bits = 1;
break;
case GX_FONT_FORMAT_2BPP:
font_bits = 2;
break;
case GX_FONT_FORMAT_4BPP:
font_bits = 4;
break;
case GX_FONT_FORMAT_8BPP:
font_bits = 8;
break;
}
return font_bits;
}
///////////////////////////////////////////////////////////////////////////////
BOOL IsFontBitsSupported(INT font_bits)
{
switch (font_bits)
{
case 1:
case 4:
case 8:
return TRUE;
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
int DuplicatesCount(GX_UBYTE *data, int width, int index)
{
int count = 1;
GX_UBYTE pre;
pre = data[index++];
while (index < width)
{
if (pre == data[index])
{
pre = data[index];
count++;
}
else
{
break;
}
index++;
}
return count;
}
///////////////////////////////////////////////////////////////////////////////
int RleEncode(GX_UBYTE *put_data, GX_UBYTE *get_data, int width, int height)
{
int count = 0;
int raw_count = 0;
int put_index = 0;
int row;
int compressed_size = 0;
for (row = 0; row < height; row++)
{
int index = 0;
while (index < width)
{
count = DuplicatesCount(get_data, width, index);
if (count >= 3)
{
if (raw_count)
{
compressed_size += raw_count + 1;
if (put_data)
{
*put_data++ = (raw_count - 1) & 0x7f;
while (raw_count)
{
*put_data++ = get_data[index - raw_count];
raw_count--;
}
}
else
{
raw_count = 0;
}
}
if (count > 128)
{
count = 128;
}
if (put_data)
{
*put_data++ = (count - 1) | 0x80;
*put_data++ = get_data[index];
}
compressed_size += 2;
index += count;
}
else
{
raw_count++;
index++;
if ((raw_count == 128) || (index == width))
{
compressed_size += raw_count + 1;
if (put_data)
{
*put_data++ = (raw_count - 1) & 0x7f;
while (raw_count)
{
*put_data++ = get_data[index - raw_count];
raw_count--;
}
}
else
{
raw_count = 0;
}
}
}
}
get_data += width;
}
return compressed_size;
}
///////////////////////////////////////////////////////////////////////////////
void RleEncodeGlyphData(GX_COMPRESSED_GLYPH *glyph, int bits_per_pix)
{
GX_UBYTE *pGet = (GX_UBYTE *)glyph->gx_glyph_map;
int pitch = GetRowPitch(glyph->gx_glyph_width, bits_per_pix);
int data_size = pitch * glyph->gx_glyph_height;
int compressed_size = RleEncode(NULL, pGet, pitch, glyph->gx_glyph_height);
if (compressed_size < data_size)
{
glyph->gx_glyph_map_size = compressed_size | 0x8000;
UCHAR *pPut = new UCHAR[compressed_size];
pGet = (GX_UBYTE *)glyph->gx_glyph_map;
RleEncode((GX_UBYTE *)pPut, pGet, pitch, glyph->gx_glyph_height);
delete glyph->gx_glyph_map;
glyph->gx_glyph_map = pPut;
}
else
{
glyph->gx_glyph_map_size = data_size;
}
}
///////////////////////////////////////////////////////////////////////////////
GX_UBYTE* RleDecodeGlyphData(GX_COMPRESSED_GLYPH* glyph, int bits_per_pix)
{
int pitch = GetRowPitch(glyph->gx_glyph_width, bits_per_pix);
int data_size = pitch * glyph->gx_glyph_height;
if (!data_size)
{
return GX_NULL;
}
GX_UBYTE* put_data = new GX_UBYTE[data_size];
GX_UBYTE* put_row = put_data;
GX_UBYTE* put;
GX_CONST GX_UBYTE* glyph_data = glyph->gx_glyph_map;
int row, col;
GX_UBYTE count;
GX_UBYTE alpha;
for (row = 0; row < glyph->gx_glyph_height; row++)
{
col = 0;
put = put_row;
while (col < pitch)
{
count = *glyph_data++;
if (count & 0x80)
{
count = (count & 0x7f) + 1;
alpha = *glyph_data++;
while (count--)
{
*put = alpha;
put++;
col++;
}
}
else
{
count++;
while (count--)
{
alpha = *glyph_data++;
*put = alpha;
put++;
col++;
}
}
}
put_row += pitch;
}
return put_data;
}
///////////////////////////////////////////////////////////////////////////////
void CopyGlyphData(GX_GLYPH *put_glyph, GXS_FONT_DATA &glyph, int bits_per_pix, BOOL reversed_order)
{
int bitmap_size;
int row;
int col;
int num_bits;
UCHAR *read_data;
UCHAR *write_data;
UCHAR val;
UCHAR in_byte;
UCHAR in_mask;
UCHAR out_mask;
put_glyph->gx_glyph_advance = glyph.gx_glyph_advance;
put_glyph->gx_glyph_ascent = glyph.gx_glyph_ascent;
put_glyph->gx_glyph_descent = glyph.gx_glyph_descent;
put_glyph->gx_glyph_height = ToUByte(glyph.height);
put_glyph->gx_glyph_width = ToUByte(glyph.width);
put_glyph->gx_glyph_leading = glyph.gx_glyph_leading;
bitmap_size = GetRowPitch(glyph.width, bits_per_pix) * glyph.height;
if(bitmap_size == 0)
{
return;
}
put_glyph->gx_glyph_map = new UCHAR[bitmap_size];
switch(bits_per_pix)
{
case 1:
write_data = (UCHAR *) put_glyph->gx_glyph_map;
for (row = 0; row < glyph.height; row++)
{
num_bits = glyph.width;
read_data = glyph.gx_glyph_data + (row * glyph.pitch);
while(num_bits)
{
val = *read_data;
if (reversed_order)
{
// swap the bit order:
in_byte = val;
val = 0;
in_mask = 0x80;
out_mask = 0x01;
while(in_mask)
{
if (in_byte & in_mask)
{
val |= out_mask;
}
in_mask >>= 1;
out_mask <<= 1;
}
}
if (num_bits > 8)
{
*write_data++ = val;
num_bits -= 8;
read_data++;
}
else
{
*write_data++ = val;
num_bits = 0;
}
}
}
break;
case 4:
// convert 8 bit incoming data to 4 bit outgoing data
read_data = glyph.gx_glyph_data;
write_data = (UCHAR *) put_glyph->gx_glyph_map;
for (row = 0; row < glyph.height; row++)
{
val = 0;
for (col = 0; col < glyph.width; col++)
{
in_byte = *read_data++;
if (col & 1)
{
if (reversed_order)
{
val |= (in_byte & 0xf0); // keep high nibble
}
else
{
val |= (in_byte >> 4);
}
*write_data++ = val;
}
else
{
if (reversed_order)
{
val = in_byte >> 4;
}
else
{
val = in_byte & 0xf0;
}
}
}
// check for odd width, if so write out last pixel
if (glyph.width & 1)
{
*write_data++ = val;
}
}
break;
default:
memcpy_s((UCHAR *) put_glyph->gx_glyph_map, bitmap_size, glyph.gx_glyph_data, bitmap_size);
break;
}
}
///////////////////////////////////////////////////////////////////////////////
void GetFontMetrics(GXS_FONT_LIBRARY_HANDLE *handle,
int height, FONT_METRICS &metrics, int firstchar, int lastchar, int bits_per_pix, FontCharMap &map)
{
int use_char;
GXS_FONT_DATA glyph;
GX_BOOL anti_alias = GX_TRUE;
if (bits_per_pix == 1)
{
anti_alias = GX_FALSE;
}
for (use_char = firstchar; use_char <= lastchar; use_char++ )
{
if (!map.Test(use_char))
{
continue;
}
gx_studio_font_util_get_glyph(handle, use_char,
height, anti_alias, &glyph);
if(glyph.height > metrics.max_height)
{
metrics.max_height = glyph.height;
}
if(glyph.gx_glyph_ascent > metrics.ascent)
{
metrics.ascent = glyph.gx_glyph_ascent;
}
if(glyph.gx_glyph_descent > metrics.descent)
{
metrics.descent = glyph.gx_glyph_descent;
}
}
if (metrics.max_height < (metrics.ascent + metrics.descent))
{
metrics.max_height = (metrics.ascent + metrics.descent);
}
}
///////////////////////////////////////////////////////////////////////////////
GX_FONT *MakeFontPage(GXS_FONT_LIBRARY_HANDLE *handle,
int height, FONT_METRICS &metrics, int firstchar, int lastchar, FontCharMap &map, res_info *info, int display)
{
int use_char;
int char_count;
GXS_FONT_DATA glyph;
GX_FONT *new_font;
GX_GLYPH *glyph_array;
GX_GLYPH *put_glyph;
GX_COMPRESSED_GLYPH *compressed_glyph_array;
GX_COMPRESSED_GLYPH *put_compressed_glyph;
GX_BOOL anti_alias = GX_TRUE;
INT bits_per_pix = info->font_bits;
/* Kerning glyph */
GX_BOOL reversed_order = GX_FALSE;
GX_KERNING_GLYPH *kerning_glyph_array;
GX_KERNING_GLYPH *put_kerning_glyph;
studiox_project *project = GetOpenProject();
if (!project)
{
return NULL;
}
char_count = lastchar - firstchar + 1;
new_font = new GX_FONT;
new_font->gx_font_first_glyph = firstchar;
new_font->gx_font_last_glyph = lastchar;
new_font->gx_font_next_page = NULL;
if (!IsRenesasDave2D(GetOpenProject()))
{
/* Compressed mode is only supported by Renesas dave2d. */
info->compress = FALSE;
}
if (info->compress)
{
compressed_glyph_array = new GX_COMPRESSED_GLYPH[char_count];
memset(compressed_glyph_array, 0, char_count * sizeof(GX_COMPRESSED_GLYPH));
new_font->gx_font_glyphs.gx_font_compressed_glyphs = compressed_glyph_array;
}
else
{
if (info->font_kerning)
{
if (FT_HAS_KERNING(handle->face))
{
/* allocate memory for kerning glyph */
kerning_glyph_array = new GX_KERNING_GLYPH[char_count];
memset(kerning_glyph_array, 0, char_count * sizeof(GX_KERNING_GLYPH));
new_font->gx_font_glyphs.gx_font_kerning_glyphs = kerning_glyph_array;
}
else
{
/* Set kerning flag to false as the font does not contain kerning information. */
info->font_kerning = FALSE;
glyph_array = new GX_GLYPH[char_count];
memset(glyph_array, 0, char_count * sizeof(GX_GLYPH));
new_font->gx_font_glyphs.gx_font_normal_glyphs = glyph_array;
}
}
else
{
glyph_array = new GX_GLYPH[char_count];
memset(glyph_array, 0, char_count * sizeof(GX_GLYPH));
new_font->gx_font_glyphs.gx_font_normal_glyphs = glyph_array;
}
}
switch(bits_per_pix)
{
case 1:
new_font->gx_font_format = GX_FONT_FORMAT_1BPP;
anti_alias = GX_FALSE;
if (IsDave2dFontFormat(project, display))
{
reversed_order = TRUE;
}
break;
case 4:
new_font->gx_font_format = GX_FONT_FORMAT_4BPP;
if (IsDave2dFontFormat(project, display))
{
reversed_order = TRUE;
}
break;
case 8:
new_font->gx_font_format = GX_FONT_FORMAT_8BPP;
break;
}
if (reversed_order)
{
new_font->gx_font_format |= GX_FONT_FORMAT_REVERSED_ORDER;
}
if (info->compress)
{
new_font->gx_font_format |= GX_FONT_FORMAT_COMPRESSED;
}
if (info->font_kerning)
{
new_font->gx_font_format |= GX_FONT_FORMAT_KERNING;
}
for (use_char = firstchar; use_char <= lastchar; use_char++)
{
if (!map.Test(use_char))
{
continue;
}
gx_studio_font_util_get_glyph(handle, use_char,
height, anti_alias, &glyph);
if (info->compress)
{
put_compressed_glyph = &(compressed_glyph_array)[use_char - firstchar];
CopyGlyphData((GX_GLYPH *)put_compressed_glyph, glyph, bits_per_pix, reversed_order);
//Compress glyph data
RleEncodeGlyphData(put_compressed_glyph, bits_per_pix);
}
else
{
if (info->font_kerning)
{
put_kerning_glyph = &kerning_glyph_array[use_char - firstchar];
CopyGlyphData((GX_GLYPH *)put_kerning_glyph, glyph, bits_per_pix, reversed_order);
if (use_char <= 0xff)
{
gx_studio_font_util_get_kerning_info(handle, use_char, firstchar, lastchar, put_kerning_glyph, &map);
}
}
else
{
put_glyph = &glyph_array[use_char - firstchar];
CopyGlyphData(put_glyph, glyph, bits_per_pix, reversed_order);
}
}
}
new_font->gx_font_prespace = 0; /* Line spacing above, pixels */
new_font->gx_font_postspace = 0;
new_font->gx_font_line_height = metrics.max_height;
new_font->gx_font_baseline = metrics.ascent;
return new_font;
}
///////////////////////////////////////////////////////////////////////////////
GX_FONT *MakeFont(res_info *info, int display, BOOL warn_on_error)
{
font_page_info *pages = info->font_pages;
int page_count;
int max_char_count;
int max_char;
int height = info->font_height;
int bits_per_pix = info->font_bits;
FontCharMap char_map;
FONT_METRICS metrics;
int page;
GXS_FONT_LIBRARY_HANDLE *handle;
GX_FONT *head_page = NULL;
GX_FONT *last_page = NULL;
studiox_project* project = GetOpenProject();
if(!project)
{
return NULL;
}
// GotoProjectDirectory();
CString abspath = MakeAbsolutePathname(info->pathinfo);
if (gx_studio_font_util_open(CT2A(abspath.GetString()), &handle) != 0)
{
if (warn_on_error)
{
CString msg;
msg.Format(_T("Font Name = %s\nFailed to open TrueType font file: %s"), info->name, abspath);
ErrorMsg(msg);
}
return NULL;
}
if (info->font_support_extended_unicode)
{
// Use 21-bit character map
char_map.SetMapSize(262144);
page_count = NUM_FONT_CHAR_RANGES + NUM_FONT_EXTENDED_CHAR_RANGES;
// extended unicode range is between [0x10000, 0x10ffff],
// the total supported character count is 0x110000
max_char_count = GX_MAX_GLYPH_CODE + 1;
max_char = 0x10ffff;
}
else
{
page_count = NUM_FONT_CHAR_RANGES;
max_char_count = 0xffff;
max_char = 0xffff;
}
if (info->font_pages_count)
{
page_count = info->font_pages_count;
}
for (page = 0; page < page_count; page++)
{
// map character ranges the user has turned on
if (pages[page].enabled && pages[page].first_char <= pages[page].last_char)
{
char_map.Set(pages[page].first_char, pages[page].last_char);
}
}
if (info->font_charset_include_string_table)
{
// add characters used in string table
FontCharMap *tmap = GetActiveCharacterMap(FALSE);
if (tmap)
{
char_map.Overlay(tmap);
}
}
if (char_map.Test(0xe00, 0xe7f))
{
// Detected thai glyph code
if (char_map.Test(0x0e33))
{
// The vowel sara am will be split into the character nikhahit and sara aa if it is
// appended to a consonant.
char_map.Set(0x0e4d, 0x0e4d);
char_map.Set(0x0e32, 0x0e32);
}
// add private user area for thai glyph shaping
char_map.Set(0xf700, 0xf71a);
}
if (char_map.Test(0x600, 0x6ff))
{
// add arabic presentation forms-b for arabic shaping
char_map.Set(0xfe70, 0xfeff);
// add arabic presentation forms-a for persion shaping
char_map.Set(0xfb50, 0xfbff);
}
if (char_map.IsEmpty())
{
gx_studio_font_util_close(handle);
return NULL;
}
int first_char, last_char;
metrics.ascent = metrics.descent = metrics.max_height = 0;
// first get the font metrics
for (first_char = 0; first_char <= max_char; first_char++)
{
if (char_map.Test(first_char, first_char))
{
GetFontMetrics(handle, height, metrics, first_char, first_char, bits_per_pix, char_map);
}
}
// now render actual font data pages
GX_FONT *newpage;
for (first_char = 0; first_char <= max_char;)
{
last_char = first_char + 127;
if (last_char > max_char)
{
last_char = max_char;
}
if (char_map.Test(first_char, last_char))
{
// Make font for characters that defined in character map but not in character ranges.
newpage = MakeFontPage(handle, height, metrics, first_char, last_char, char_map, info, display);
if (newpage)
{
if (!head_page)
{
head_page = newpage;
}
if (last_page)
{
last_page->gx_font_next_page = newpage;
}
last_page = newpage;
}
}
first_char = last_char + 1;
}
gx_studio_font_util_close(handle);
return head_page;
}
///////////////////////////////////////////////////////////////////////////////
BOOL InsertStringTableGlyphs(FontCharMap *char_map, res_info *info, int display)
{
BOOL status = TRUE;
BOOL found_page;
BOOL anti_alias = FALSE;
int height = info->font_height;
int bits_per_pix = info->font_bits;
DWORD char_val;
DWORD maxval = char_map->GetMapSize() << 3;
GXS_FONT_LIBRARY_HANDLE *handle;
GXS_FONT_DATA glyph;
GX_FONT *head_page = NULL;
GX_FONT *last_page = NULL;
if (!char_map)
{
return FALSE;
}
CString abspath = MakeAbsolutePathname(info->pathinfo);
if (gx_studio_font_util_open(CT2A(abspath.GetString()), &handle) != 0)
{
return FALSE;
}
if (bits_per_pix > 1)
{
anti_alias = TRUE;
}
BOOL reversed_order = FALSE;
switch (bits_per_pix)
{
case 1:
case 4:
if (IsDave2dFontFormat(GetOpenProject(), display))
{
reversed_order = TRUE;
}
break;
}
for (char_val = 0; char_val < maxval; char_val++)
{
if (char_map->Test(char_val))
{
GX_FONT *page = info->font;
found_page = FALSE;
while(page)
{
if (page->gx_font_first_glyph <= char_val &&
page->gx_font_last_glyph >= char_val)
{
found_page = TRUE;
if (info->font_kerning)
{
GX_KERNING_GLYPH *put_glyph = (GX_KERNING_GLYPH *) &page->gx_font_glyphs.gx_font_kerning_glyphs[char_val - page->gx_font_first_glyph];
if ((!put_glyph->gx_glyph_map) && (put_glyph->gx_glyph_advance == 0))
{
// get the glyph data, insert into font
if (gx_studio_font_util_get_glyph(handle, char_val, height, anti_alias, &glyph) != GXS_FONT_UTIL_SUCCESS)
{
status = FALSE;
break;
}
CopyGlyphData((GX_GLYPH *)put_glyph, glyph, bits_per_pix, reversed_order);
if (put_glyph->gx_kerning_table)
{
delete[] put_glyph->gx_kerning_table;
put_glyph->gx_kerning_table = NULL;
if (char_val <= 0xff)
{
gx_studio_font_util_get_kerning_info(handle, char_val, page->gx_font_first_glyph, page->gx_font_last_glyph, put_glyph, char_map);
}
}
}
}
else if (page->gx_font_format & GX_FONT_FORMAT_COMPRESSED)
{
GX_COMPRESSED_GLYPH *put_glyph = (GX_COMPRESSED_GLYPH *) &page->gx_font_glyphs.gx_font_compressed_glyphs[char_val - page->gx_font_first_glyph];
if ((!put_glyph->gx_glyph_map) && (put_glyph->gx_glyph_advance == 0))
{
// get the glyph data, insert into font
if (gx_studio_font_util_get_glyph(handle, char_val, height, anti_alias, &glyph) != GXS_FONT_UTIL_SUCCESS)
{
status = FALSE;
break;
}
CopyGlyphData((GX_GLYPH *)put_glyph, glyph, bits_per_pix, reversed_order);
//Compress glyph data
RleEncodeGlyphData(put_glyph, bits_per_pix);
}
}
else
{
GX_GLYPH *put_glyph = (GX_GLYPH *)&page->gx_font_glyphs.gx_font_normal_glyphs[char_val - page->gx_font_first_glyph];
if ((!put_glyph->gx_glyph_map) && (put_glyph->gx_glyph_advance == 0))
{
// get the glyph data, insert into font
if (gx_studio_font_util_get_glyph(handle, char_val, height, anti_alias, &glyph) != GXS_FONT_UTIL_SUCCESS)
{
status = FALSE;
break;
}
CopyGlyphData(put_glyph, glyph, bits_per_pix, reversed_order);
}
}
break;
}
page = (GX_FONT *) page->gx_font_next_page;
}
if ((!found_page) || (status != TRUE))
{
status = FALSE;
break;
}
}
}
gx_studio_font_util_close(handle);
return status;
}
///////////////////////////////////////////////////////////////////////////////
void DestroyFont(GX_FONT *font)
{
GX_GLYPH *glyph;
GX_COMPRESSED_GLYPH *compressed_glyph;
GX_KERNING_GLYPH *kering_glyph;
const GX_FONT *next_page = NULL;
while (font)
{
next_page = font->gx_font_next_page;
int num_chars = font->gx_font_last_glyph - font->gx_font_first_glyph + 1;
if (font->gx_font_glyphs.gx_font_normal_glyphs)
{
glyph = (GX_GLYPH *) font->gx_font_glyphs.gx_font_normal_glyphs;
for (int index = 0; index < num_chars; index++)
{
if (glyph->gx_glyph_map)
{
delete [] glyph->gx_glyph_map;
glyph->gx_glyph_map = NULL;
}
if (font->gx_font_format & GX_FONT_FORMAT_COMPRESSED)
{
compressed_glyph = (GX_COMPRESSED_GLYPH *)glyph;
compressed_glyph++;
glyph = (GX_GLYPH *)compressed_glyph;
}
else if (font->gx_font_format & GX_FONT_FORMAT_KERNING)
{
kering_glyph = (GX_KERNING_GLYPH *)glyph;
if (kering_glyph->gx_kerning_table)
{
delete[] kering_glyph->gx_kerning_table;
kering_glyph->gx_kerning_table = NULL;
}
kering_glyph++;
glyph = (GX_GLYPH *)kering_glyph;
}
else
{
glyph++;
}
}
delete [] font->gx_font_glyphs.gx_font_normal_glyphs;
font->gx_font_glyphs.gx_font_normal_glyphs = NULL;
}
delete font;
font = (GX_FONT *) next_page;
}
}
///////////////////////////////////////////////////////////////////////////////
GX_CHAR_CODE FindFirstValidGlyph(const GX_FONT *page, GX_CHAR_CODE glyph_start)
{
GX_CHAR_CODE index;
GX_COMPRESSED_GLYPH *compressed_glyph;
GX_KERNING_GLYPH *kerning_glyph;
while(1)
{
if (glyph_start > page->gx_font_last_glyph)
{
break;
}
index = glyph_start - page->gx_font_first_glyph;
if (page->gx_font_format & GX_FONT_FORMAT_COMPRESSED)
{
compressed_glyph = (GX_COMPRESSED_GLYPH *)page->gx_font_glyphs.gx_font_compressed_glyphs;
if (compressed_glyph[index].gx_glyph_map != NULL ||
compressed_glyph[index].gx_glyph_advance != 0)
{
break;
}
}
else if (page->gx_font_format & GX_FONT_FORMAT_KERNING)
{
kerning_glyph = (GX_KERNING_GLYPH *)page->gx_font_glyphs.gx_font_kerning_glyphs;
if (kerning_glyph[index].gx_glyph_map != NULL ||
kerning_glyph[index].gx_glyph_advance != 0)
{
break;
}
}
else
{
if (page->gx_font_glyphs.gx_font_normal_glyphs[index].gx_glyph_map != NULL ||
page->gx_font_glyphs.gx_font_normal_glyphs[index].gx_glyph_advance != 0)
{
break;
}
}
glyph_start++;
}
return glyph_start;
}
#define MAX_FONT_DEAD_BLOCK 64
///////////////////////////////////////////////////////////////////////////////
GX_CHAR_CODE FindDeadGlyphBlock(const GX_FONT *page, GX_CHAR_CODE glyph_end)
{
GX_CHAR_CODE dead_count = 0;
GX_CHAR_CODE stop_point = glyph_end;
GX_CHAR_CODE index;
GX_CONST GX_COMPRESSED_GLYPH *compressed_glyph;
GX_CONST GX_KERNING_GLYPH *kerning_glyph;
index = stop_point;
if (page->gx_font_format & GX_FONT_FORMAT_COMPRESSED)
{
compressed_glyph = page->gx_font_glyphs.gx_font_compressed_glyphs;
while (index <= page->gx_font_last_glyph)
{
if (compressed_glyph[index - page->gx_font_first_glyph].gx_glyph_map == NULL &&
compressed_glyph[index - page->gx_font_first_glyph].gx_glyph_advance == 0)
{
dead_count++;
if (dead_count > MAX_FONT_DEAD_BLOCK)
{
break;
}
}
else
{
stop_point = index;
dead_count = 0;
}
index++;
}
}
else if (page->gx_font_format & GX_FONT_FORMAT_KERNING)
{
kerning_glyph = page->gx_font_glyphs.gx_font_kerning_glyphs;
while (index <= page->gx_font_last_glyph)
{
if (kerning_glyph[index - page->gx_font_first_glyph].gx_glyph_map == NULL &&
kerning_glyph[index - page->gx_font_first_glyph].gx_glyph_advance == 0)
{
dead_count++;
if (dead_count > MAX_FONT_DEAD_BLOCK)
{
break;
}
}
else
{
stop_point = index;
dead_count = 0;
}
index++;
}
}
else
{
while (index <= page->gx_font_last_glyph)
{
if (page->gx_font_glyphs.gx_font_normal_glyphs[index - page->gx_font_first_glyph].gx_glyph_map == NULL &&
page->gx_font_glyphs.gx_font_normal_glyphs[index - page->gx_font_first_glyph].gx_glyph_advance == 0)
{
dead_count++;
if (dead_count > MAX_FONT_DEAD_BLOCK)
{
break;
}
}
else
{
stop_point = index;
dead_count = 0;
}
index++;
}
}
return stop_point;
}
///////////////////////////////////////////////////////////////////////////////
void CopyGlyphs(GX_FONT *dest, const GX_FONT *src)
{
int char_count = dest->gx_font_last_glyph - dest->gx_font_first_glyph + 1;
GX_GLYPH *glyph_array = new GX_GLYPH[char_count];
memset(glyph_array, 0, char_count * sizeof(GX_GLYPH));
dest->gx_font_glyphs.gx_font_normal_glyphs = glyph_array;
GX_GLYPH *put = glyph_array;
const GX_GLYPH *get = &src->gx_font_glyphs.gx_font_normal_glyphs[dest->gx_font_first_glyph - src->gx_font_first_glyph];
int bitmap_size = 0;
while(char_count--)
{
*put = *get;
put->gx_glyph_map = NULL;
bitmap_size = GetRowPitch(put->gx_glyph_width, GetFontBits(dest->gx_font_format));
bitmap_size *= put->gx_glyph_height;
if(bitmap_size != 0)
{
put->gx_glyph_map = new UCHAR[bitmap_size];
memcpy_s((GX_UBYTE *) put->gx_glyph_map, bitmap_size, get->gx_glyph_map, bitmap_size);
}
else
{
put->gx_glyph_map = GX_NULL;
}
put++;
get++;
}
}
///////////////////////////////////////////////////////////////////////////////
void CopyKerningGlyphs(GX_FONT *dest, const GX_FONT *src)
{
int char_count = dest->gx_font_last_glyph - dest->gx_font_first_glyph + 1;
GX_KERNING_GLYPH *glyph_array = new GX_KERNING_GLYPH[char_count];
memset(glyph_array, 0, char_count * sizeof(GX_KERNING_GLYPH));
dest->gx_font_glyphs.gx_font_kerning_glyphs = glyph_array;
GX_KERNING_GLYPH *put = glyph_array;
const GX_KERNING_GLYPH *get = &src->gx_font_glyphs.gx_font_kerning_glyphs[dest->gx_font_first_glyph - src->gx_font_first_glyph];
int bitmap_size = 0;
while (char_count--)
{
*put = *get;
bitmap_size = GetRowPitch(put->gx_glyph_width, GetFontBits(dest->gx_font_format));
bitmap_size *= put->gx_glyph_height;
if (bitmap_size != 0)
{
put->gx_glyph_map = new UCHAR[bitmap_size];
memcpy_s((GX_UBYTE *)put->gx_glyph_map, bitmap_size, get->gx_glyph_map, bitmap_size);
}
else
{
put->gx_glyph_map = GX_NULL;
}
put -> gx_kerning_table = GX_NULL;
if (get -> gx_kerning_table)
{
/* Copy kerning table if there's one */
INT table_size = *(get->gx_kerning_table);
table_size = 1 + (table_size * (sizeof(GX_UBYTE) + sizeof(GX_CHAR)));
put->gx_kerning_table = new GX_UBYTE[table_size];
memcpy_s((GX_UBYTE *)put->gx_kerning_table, table_size, get->gx_kerning_table, table_size);
}
put++;
get++;
}
}
///////////////////////////////////////////////////////////////////////////////
void CopyCompressedGlyphs(GX_FONT *dest, const GX_FONT *src)
{
int char_count = dest->gx_font_last_glyph - dest->gx_font_first_glyph + 1;
GX_COMPRESSED_GLYPH *glyph_array = new GX_COMPRESSED_GLYPH[char_count];
memset(glyph_array, 0, char_count * sizeof(GX_COMPRESSED_GLYPH));
dest->gx_font_glyphs.gx_font_compressed_glyphs = glyph_array;
GX_COMPRESSED_GLYPH *put = glyph_array;
const GX_COMPRESSED_GLYPH *get = &src->gx_font_glyphs.gx_font_compressed_glyphs[dest->gx_font_first_glyph - src->gx_font_first_glyph];
int bitmap_size = 0;
while (char_count--)
{
*put = *get;
bitmap_size = put->gx_glyph_map_size & 0x7fff;
if (bitmap_size != 0)
{
put->gx_glyph_map = new UCHAR[bitmap_size];
memcpy_s((GX_UBYTE *)put->gx_glyph_map, bitmap_size, get->gx_glyph_map, bitmap_size);
}
else
{
put->gx_glyph_map = GX_NULL;
}
put++;
get++;
}
}
///////////////////////////////////////////////////////////////////////////////
GX_FONT *optimize_font(GX_FONT *src_font)
{
GX_FONT *head_page = NULL;
GX_FONT *last_page = NULL;
GX_FONT *current_page = NULL;
const GX_FONT *src_page = src_font;
while (src_page)
{
GX_CHAR_CODE first_glyph = src_page->gx_font_first_glyph;
GX_CHAR_CODE last_glyph = src_page->gx_font_last_glyph;
while(first_glyph <= src_page->gx_font_last_glyph)
{
// skip leading NULL glyphs
first_glyph = FindFirstValidGlyph(src_page, first_glyph);
if (first_glyph <= src_page->gx_font_last_glyph)
{
// stop if there is a large dead section
last_glyph = FindDeadGlyphBlock(src_page, first_glyph);
// make a font page for first to last:
current_page = new GX_FONT;
memset(current_page, 0, sizeof(GX_FONT));
current_page->gx_font_baseline = src_page->gx_font_baseline;
current_page->gx_font_first_glyph = first_glyph;
current_page->gx_font_last_glyph = last_glyph;
current_page->gx_font_format = src_page->gx_font_format;
current_page->gx_font_line_height = src_page->gx_font_line_height;
current_page->gx_font_postspace = src_page->gx_font_postspace;
current_page->gx_font_prespace = src_page->gx_font_prespace;
if (src_page->gx_font_format & GX_FONT_FORMAT_COMPRESSED)
{
CopyCompressedGlyphs(current_page, src_page);
}
else if(src_page->gx_font_format & GX_FONT_FORMAT_KERNING)
{
CopyKerningGlyphs(current_page, src_page);
}
else
{
CopyGlyphs(current_page, src_page);
}
if (last_page)
{
last_page->gx_font_next_page = current_page;
}
else
{
head_page = current_page;
}
last_page = current_page;
first_glyph = last_glyph + 1;
}
}
src_page = src_page->gx_font_next_page;
}
return head_page;
}
///////////////////////////////////////////////////////////////////////////////
GX_FONT *MakeOptimizedFont(res_info *info, int display, BOOL warn_on_error)
{
GX_FONT *optimized = GX_NULL;
GX_FONT *new_font = MakeFont(info, display, warn_on_error);
if (new_font)
{
optimized = optimize_font(new_font);
DestroyFont(new_font);
}
return optimized;
}
///////////////////////////////////////////////////////////////////////////////
INT GetFontStorage(res_info *info, studiox_project *project, int display)
{
// return the size (in byte) of output font
const GX_FONT *font_page;
const GX_FONT *head_page;
long datasize = 0;
long kerning_datasize = 0;
GX_CHAR_CODE charval;
GX_CHAR_CODE index;
const GX_GLYPH *glyph;
const GX_COMPRESSED_GLYPH *compressed_glyph;
const GX_KERNING_GLYPH *kerning_glyph;
GX_UBYTE Kerning_pairs_count;
int pitch;
/* Make the font again, with optimization */
head_page = MakeOptimizedFont(info, display);
if (!head_page)
{
return datasize;
}
font_page = head_page;
/* Calculate font size. */
while (font_page)
{
for (charval = font_page->gx_font_first_glyph; charval <= font_page->gx_font_last_glyph; charval++)
{
index = charval - font_page->gx_font_first_glyph;
if (font_page->gx_font_format & GX_FONT_FORMAT_COMPRESSED)
{
compressed_glyph = &font_page->gx_font_glyphs.gx_font_compressed_glyphs[index];
datasize += (compressed_glyph->gx_glyph_map_size & 0x7fff);
}
else if (font_page->gx_font_format & GX_FONT_FORMAT_KERNING)
{
kerning_glyph = &font_page->gx_font_glyphs.gx_font_kerning_glyphs[index];
pitch = GetRowPitch(kerning_glyph->gx_glyph_width, info->font_bits);
datasize += pitch * kerning_glyph->gx_glyph_height;
if (kerning_glyph->gx_kerning_table)
{
Kerning_pairs_count = *(kerning_glyph->gx_kerning_table);
/* Add the kerning table size. */
kerning_datasize += 1 + Kerning_pairs_count * (sizeof(GX_CHAR) + sizeof(GX_UBYTE));
}
}
else
{
glyph = &font_page->gx_font_glyphs.gx_font_normal_glyphs[index];
pitch = GetRowPitch(glyph->gx_glyph_width, info->font_bits);
datasize += pitch * glyph->gx_glyph_height;
}
}
font_page = font_page->gx_font_next_page;
}
DestroyFont((GX_FONT *)head_page);
/* if kerning info size is bigger than 5 kB*/
if (kerning_datasize > 5120)
{
if (kerning_datasize > datasize)
{
/* kerning info data size is bigger than the glyph size.
Show notification dialog that the font size is so large. */
CString message ("Generate kerning info of font ");
message += info->name;
message += " is selected and it will take lots of memory. Make sure it is wanted.";
Notify(CW2A(message));
}
}
return (datasize + kerning_datasize);
}
////////////////////////////////////////////////////////////////////////////////
INT GetPixelmapStorage(res_info *info)
{
int storage = 0;
if (info->raw)
{
CString path = MakeAbsolutePathname(info->pathinfo);
FILE *file = _tfopen(path.GetBuffer(), _T("rb"));
if (!file)
{
return 0;
}
fseek(file, 0, SEEK_END);
storage = ftell(file);
fclose(file);
}
else
{
GX_PIXELMAP *map;
for (int index = 0; index < info->GetPixelmapFrameCount(); index++)
{
map = info->GetPixelmap(index);
if (map)
{
storage += map->gx_pixelmap_data_size;
if (map->gx_pixelmap_aux_data_size)
{
if (info->output_color_format != GX_COLOR_FORMAT_8BIT_PALETTE ||
(info->palette_type == PALETTE_TYPE_PRIVATE && index == 0))
{
storage += map->gx_pixelmap_aux_data_size;
}
}
}
}
}
return storage;
}