mirror of
https://github.com/QB64-Phoenix-Edition/QB64pe.git
synced 2024-09-04 11:10:15 +00:00
350 lines
11 KiB
C
350 lines
11 KiB
C
//[ ]what if no glyph index could be found and it returns 0??
|
|
//[ ]FT_Done_Face
|
|
//[ ]Check for memory leaks
|
|
|
|
#ifndef DEPENDENCY_LOADFONT
|
|
// Stubs
|
|
int32 FontRenderTextUTF32(int32 i, uint32 *codepoint, int32 codepoints, int32 options, uint8 **out_data, int32 *out_x, int32 *out_y, int32 *out_x_pre_increment,
|
|
int32 *out_x_post_increment) {
|
|
return NULL;
|
|
}
|
|
int32 FontLoad(uint8 *content_original, int32 content_bytes, int32 default_pixel_height, int32 which_font, int32 options) { return NULL; }
|
|
int32 FontRenderTextASCII(int32 i, uint8 *codepoint, int32 codepoints, int32 options, uint8 **out_data, int32 *out_x, int32 *out_y, int32 *out_x_pre_increment,
|
|
int32 *out_x_post_increment) {
|
|
return NULL;
|
|
}
|
|
int32 FontWidth(int32 i) { return NULL; }
|
|
void FontFree(int32 i) { return; }
|
|
#else
|
|
|
|
# ifdef QB64_BACKSLASH_FILESYSTEM
|
|
# include "src\\freetypeamalgam.h"
|
|
# else
|
|
# include "src/freetypeamalgam.h"
|
|
# endif
|
|
FT_Library ft_library;
|
|
|
|
struct fonts_struct {
|
|
uint8 in_use;
|
|
uint8 *ttf_data;
|
|
int32 default_pixel_height;
|
|
uint8 bold;
|
|
uint8 italic;
|
|
uint8 underline;
|
|
uint8 monospace;
|
|
int32 monospace_width;
|
|
uint8 unicode;
|
|
//---------------------------------
|
|
FT_Face handle;
|
|
int32 baseline;
|
|
float default_pixel_height_scale;
|
|
};
|
|
fonts_struct *fonts = (fonts_struct *)malloc(1);
|
|
int32 fonts_last = 0;
|
|
|
|
struct fonts_render_struct {
|
|
uint8 *data;
|
|
int32 w;
|
|
int32 ox;
|
|
};
|
|
|
|
// Master rendering routine (to be called by all other functions)
|
|
int32 FontRenderTextUTF32(int32 i, uint32 *codepoint, int32 codepoints, int32 options, uint8 **out_data, int32 *out_x, int32 *out_y, int32 *out_x_pre_increment,
|
|
int32 *out_x_post_increment) {
|
|
// Notes:
|
|
// returns: success{1}/failure{0}
|
|
// options: 1=black{0}&white{255}
|
|
// out_x_increment: the ideal amount to move across horizontally after drawing the text
|
|
// out_data: alpha values for each pixel of the font
|
|
|
|
if (codepoints <= 0) {
|
|
*out_data = NULL;
|
|
*out_x = 0;
|
|
*out_y = fonts[i].default_pixel_height;
|
|
*out_x_pre_increment = 0;
|
|
*out_x_post_increment = 0;
|
|
if (codepoints < 0)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static int32 monochrome;
|
|
monochrome = options & 1;
|
|
|
|
static int32 codepoint_w;
|
|
static int32 codepoint_i, codepoint_ox;
|
|
static uint32 codepoint_value;
|
|
static fonts_render_struct *render;
|
|
static int32 value;
|
|
static uint8 *data1, *data2;
|
|
static int w1, h1, ox, oy; // Note: Must be 'int' type
|
|
static int32 w2, h2, ox2, oy2;
|
|
static int32 x1, y1;
|
|
static int32 x2, y2;
|
|
|
|
if (codepoints > 1) {
|
|
render = (fonts_render_struct *)malloc(sizeof(fonts_render_struct) * codepoints);
|
|
}
|
|
|
|
codepoint_w = 0;
|
|
codepoint_ox = 0;
|
|
for (codepoint_i = 0; codepoint_i < codepoints; codepoint_i++) {
|
|
codepoint_value = codepoint[codepoint_i];
|
|
|
|
static int32 glyph_index;
|
|
glyph_index = FT_Get_Char_Index(fonts[i].handle, codepoint_value);
|
|
if (!glyph_index) {
|
|
// failed!
|
|
}
|
|
if (FT_Load_Glyph(fonts[i].handle, glyph_index, FT_LOAD_DEFAULT)) {
|
|
// failed!
|
|
}
|
|
|
|
if (monochrome) {
|
|
|
|
if (FT_Render_Glyph(fonts[i].handle->glyph, FT_RENDER_MODE_MONO)) {
|
|
// failed!
|
|
}
|
|
|
|
} else {
|
|
|
|
if (FT_Render_Glyph(fonts[i].handle->glyph, FT_RENDER_MODE_NORMAL)) {
|
|
// failed!
|
|
}
|
|
}
|
|
|
|
static int32 pitch1;
|
|
pitch1 = fonts[i].handle->glyph->bitmap.pitch;
|
|
|
|
ox = fonts[i].handle->glyph->bitmap_left;
|
|
oy = 0;
|
|
h1 = fonts[i].handle->glyph->bitmap.rows;
|
|
w1 = fonts[i].handle->glyph->bitmap.width;
|
|
data1 = (uint8 *)fonts[i].handle->glyph->bitmap.buffer;
|
|
|
|
h2 = fonts[i].default_pixel_height;
|
|
|
|
w2 = fonts[i].handle->glyph->advance.x / 64; // default width
|
|
if (w2 < w1)
|
|
w2 = w1;
|
|
ox2 = 0;
|
|
if (ox > 0) {
|
|
if ((w1 + ox) > w2)
|
|
w2 = w1 + ox;
|
|
ox2 = ox;
|
|
}
|
|
if (ox < 0) { // compensate for loss of width from left shift
|
|
w2 = w2 + (-ox);
|
|
}
|
|
|
|
// Monospace resize as necessary
|
|
if (fonts[i].monospace) {
|
|
if (w2 != fonts[i].monospace_width) {
|
|
w2 = fonts[i].monospace_width;
|
|
ox = 0; // no repositioning possible
|
|
ox2 = w2 / 2 - w1 / 2; // align to centre
|
|
}
|
|
}
|
|
|
|
data2 = (uint8 *)calloc(w2 * h2, 1);
|
|
|
|
oy2 = fonts[i].baseline - fonts[i].handle->glyph->bitmap_top;
|
|
|
|
for (y1 = 0; y1 < h1; y1++) {
|
|
y2 = y1 + oy2;
|
|
if ((y2 >= 0) && (y2 < h2)) {
|
|
for (x1 = 0; x1 < w1; x1++) {
|
|
x2 = x1 + ox2;
|
|
if ((x2 >= 0) && (x2 < w2)) {
|
|
|
|
if (monochrome) {
|
|
data2[x2 + y2 * w2] = ((data1[y1 * pitch1 + x1 / 8] >> (7 - (x1 & 7))) & 1) * 255; // 1-bit
|
|
} else {
|
|
data2[x2 + y2 * w2] = data1[x1 + y1 * pitch1]; // 8-bit
|
|
}
|
|
}
|
|
} // x1
|
|
}
|
|
} // y1
|
|
|
|
// single character render?
|
|
if (codepoints == 1) {
|
|
*out_data = data2;
|
|
*out_x = w2;
|
|
*out_y = h2;
|
|
if (ox < 0)
|
|
*out_x_pre_increment = ox;
|
|
else
|
|
*out_x_pre_increment = 0;
|
|
*out_x_post_increment = 0;
|
|
return 1;
|
|
}
|
|
|
|
if (codepoint_i == 0) {
|
|
if (ox < 0) {
|
|
*out_x_pre_increment = ox;
|
|
} else {
|
|
*out_x_pre_increment = 0;
|
|
}
|
|
} else {
|
|
if (ox < 0) { // regress codepoint_ox?
|
|
if ((codepoint_ox + ox) >= 0) {
|
|
codepoint_ox += ox;
|
|
} else {
|
|
codepoint_ox = 0;
|
|
}
|
|
}
|
|
}
|
|
render[codepoint_i].data = data2;
|
|
render[codepoint_i].w = w2;
|
|
render[codepoint_i].ox = codepoint_ox;
|
|
codepoint_ox += w2;
|
|
if (codepoint_ox > codepoint_w)
|
|
codepoint_w = codepoint_ox;
|
|
|
|
} // codepointi loop
|
|
|
|
// join&'blend' multiple codepoints
|
|
w2 = codepoint_w;
|
|
h2 = fonts[i].default_pixel_height;
|
|
data2 = (uint8 *)calloc(w2 * h2, 1);
|
|
for (codepoint_i = 0; codepoint_i < codepoints; codepoint_i++) {
|
|
|
|
data1 = render[codepoint_i].data;
|
|
w1 = render[codepoint_i].w;
|
|
h1 = h2;
|
|
|
|
ox2 = render[codepoint_i].ox;
|
|
|
|
for (y1 = 0; y1 < h1; y1++) {
|
|
y2 = y1;
|
|
for (x1 = 0; x1 < w1; x1++) {
|
|
x2 = x1 + ox2;
|
|
value = data1[x1 + y1 * w1];
|
|
if (value > data2[x2 + y2 * w2])
|
|
data2[x2 + y2 * w2] = value;
|
|
} // x1
|
|
} // y1
|
|
free(data1);
|
|
|
|
} // codepoint_i
|
|
|
|
*out_data = data2;
|
|
*out_x = w2;
|
|
*out_y = h2;
|
|
// Note: '*out_x_pre_increment' is set above
|
|
*out_x_post_increment = 0;
|
|
if (codepoints > 1)
|
|
free(render);
|
|
return 1;
|
|
}
|
|
|
|
int32 FontLoad(uint8 *content_original, int32 content_bytes, int32 default_pixel_height, int32 which_font, int32 options) {
|
|
|
|
static int32 ft_init_called = 0;
|
|
if (!ft_init_called) {
|
|
ft_init_called = 1;
|
|
if (FT_Init_FreeType(&ft_library))
|
|
exit(5633);
|
|
}
|
|
|
|
if (which_font == -1)
|
|
which_font = 0;
|
|
|
|
// options: 1=bold, 2=italic, 4=underline, 8=IGNORED, 16=monospace, 32=unicode
|
|
|
|
// get new index
|
|
static int32 i;
|
|
for (i = 1; i <= fonts_last; i++) {
|
|
if (!fonts[i].in_use) {
|
|
goto got_index;
|
|
}
|
|
} // i
|
|
fonts_last++;
|
|
i = fonts_last;
|
|
fonts = (fonts_struct *)realloc(fonts, sizeof(fonts_struct) * (fonts_last + 1));
|
|
fonts[i].in_use = 0;
|
|
got_index:
|
|
|
|
memset(&fonts[i], 0, sizeof(fonts_struct));
|
|
|
|
// duplicate content
|
|
static uint8 *content;
|
|
content = (uint8 *)malloc(content_bytes);
|
|
memcpy(content, content_original, content_bytes);
|
|
fonts[i].ttf_data = content;
|
|
|
|
if (FT_New_Memory_Face(ft_library, content, content_bytes, which_font, &fonts[i].handle))
|
|
return 0;
|
|
// Note: "Note that you must not deallocate the memory before calling FT_Done_Face."
|
|
|
|
if (FT_Set_Pixel_Sizes(fonts[i].handle, 0, default_pixel_height)) {
|
|
FT_Done_Face(fonts[i].handle);
|
|
return 0;
|
|
}
|
|
fonts[i].default_pixel_height = default_pixel_height;
|
|
|
|
/*
|
|
static float m_height; m_height=((float)fonts[i].handle->size->metrics.height)/64.0;
|
|
static float m_up; m_up=((float)fonts[i].handle->size->metrics.ascender)/64.0;
|
|
static float m_down; m_down=-(((float)fonts[i].handle->size->metrics.descender)/64.0);
|
|
static float m_char_height; m_char_height=m_up+m_down;
|
|
static float m_h; m_h=default_pixel_height;
|
|
fonts[i].baseline= (m_h/m_height) * ((m_height-m_char_height)/2.0+m_up) ;
|
|
*/
|
|
static float m_height;
|
|
m_height = ((float)fonts[i].handle->size->metrics.height) / 64.0;
|
|
static float m_up;
|
|
m_up = ((float)fonts[i].handle->size->metrics.ascender) / 64.0;
|
|
static float m_h;
|
|
m_h = default_pixel_height;
|
|
fonts[i].baseline = qbr((m_up / m_height) * m_h);
|
|
|
|
if (options & 16) {
|
|
// get the width of capital W
|
|
static uint32 cp;
|
|
cp = 87;
|
|
static uint8 *data1;
|
|
int32 w1, h1, pre_x, post_x;
|
|
FontRenderTextUTF32(i, &cp, 1, 1, &data1, &w1, &h1, &pre_x, &post_x);
|
|
fonts[i].monospace_width = w1;
|
|
free(data1);
|
|
fonts[i].monospace = 1;
|
|
} // monospace
|
|
|
|
// Note: DO NOT ADD NEW CONTENT HERE, ADD IT ABOVE MONOSPACE CHECK
|
|
|
|
fonts[i].in_use = 1;
|
|
return i;
|
|
}
|
|
|
|
void FontFree(int32 i) {
|
|
FT_Done_Face(fonts[i].handle);
|
|
free(fonts[i].ttf_data);
|
|
fonts[i].in_use = 0;
|
|
}
|
|
|
|
int32 FontRenderTextASCII(int32 i, uint8 *codepoint, int32 codepoints, int32 options, uint8 **out_data, int32 *out_x, int32 *out_y, int32 *out_x_pre_increment,
|
|
int32 *out_x_post_increment) {
|
|
static uint32 *utf32_codepoint;
|
|
static int32 retval;
|
|
if (codepoints >= 1) {
|
|
utf32_codepoint = (uint32 *)malloc(codepoints * 4);
|
|
static int32 x;
|
|
for (x = 0; x < codepoints; x++) {
|
|
utf32_codepoint[x] = codepage437_to_unicode16[codepoint[x]];
|
|
}
|
|
}
|
|
retval = FontRenderTextUTF32(i, utf32_codepoint, codepoints, options, out_data, out_x, out_y, out_x_pre_increment, out_x_post_increment);
|
|
if (codepoints > 0)
|
|
free(utf32_codepoint);
|
|
return retval;
|
|
}
|
|
|
|
int32 FontWidth(int32 i) {
|
|
if (fonts[i].monospace)
|
|
return fonts[i].monospace_width;
|
|
return 0;
|
|
}
|
|
|
|
#endif
|