added optional font handling
This commit is contained in:
parent
a08febda9d
commit
c875e59710
141
Readme.md
141
Readme.md
@ -8,13 +8,16 @@ application and does not have any direct dependencies.
|
||||
## Features
|
||||
- Immediate mode graphical user interface toolkit
|
||||
- Written in C89 (ANSI C)
|
||||
- Small codebase (~7kLOC)
|
||||
- Small codebase (~8kLOC)
|
||||
- Focus on portability, efficiency, simplicity and minimal internal state
|
||||
- No global or hidden state
|
||||
- No direct dependencies (not even libc!)
|
||||
- No direct dependencies
|
||||
- Configurable style and colors
|
||||
- UTF-8 support
|
||||
- Optional vertex buffer output
|
||||
|
||||
## Optional
|
||||
- vertex buffer output
|
||||
- font handling
|
||||
|
||||
## Gallery
|
||||
![gui demo](/screen/demo.png?raw=true)
|
||||
@ -24,11 +27,8 @@ application and does not have any direct dependencies.
|
||||
## Example
|
||||
```c
|
||||
/* setup configuration */
|
||||
struct gui_font font;
|
||||
struct gui_style style;
|
||||
font.userdata.ptr = your_font_data;
|
||||
font.height = your_font_data.height;
|
||||
font.width = your_font_string_width_callback_function;
|
||||
struct gui_user_font font = {...};
|
||||
gui_style_default(&style, GUI_DEFAULT_ALL, &font);
|
||||
|
||||
/* allocate memory to hold draw commands */
|
||||
@ -84,12 +84,13 @@ while (1) {
|
||||
Immediate mode in contrast to classical retained mode GUIs store as little state as possible
|
||||
by using procedural function calls as "widgets" instead of storing objects.
|
||||
Each "widget" function call takes hereby all its necessary data and immediately returns
|
||||
the through the user modified state back to the caller. Immediate mode graphical
|
||||
the user modified state back to the caller. Immediate mode graphical
|
||||
user interfaces therefore combine drawing and input handling into one unit
|
||||
instead of separating them like retain mode GUIs.
|
||||
|
||||
Since there is no to minimal internal state in immediate mode user interfaces,
|
||||
updates have to occur every frame which on one hand is more drawing expensive than classic
|
||||
updates have to occur every frame, on every user input update or program state change
|
||||
which on one hand is more drawing expensive than classic
|
||||
retained GUI implementations but on the other hand grants a lot more flexibility and
|
||||
support for overall layout changes. In addition without any state there is no
|
||||
duplicated state between your program, the gui and the user which greatly
|
||||
@ -97,126 +98,6 @@ simplifies code. Further traits of immediate mode graphic user interfaces are a
|
||||
code driven style, centralized flow control, easy extensibility and
|
||||
understandability.
|
||||
|
||||
### Input
|
||||
The `gui_input` struct holds the user input over the course of the frame and
|
||||
manages the complete modification of widget and panel state. To fill the
|
||||
structure with data over the frame there are a number of functions provided for
|
||||
key, motion, button and text input. The input is hereby completly independent of
|
||||
the underlying platform or way of input so even touch or other ways of input are
|
||||
possible.
|
||||
Like the panel and the buffer, input is based on an immediate mode API and
|
||||
consist of an begin sequence with `gui_input_begin` and a end sequence point
|
||||
with `gui_input_end`. All modifications can only occur between both of these
|
||||
sequence points while all outside modification provoke undefined behavior.
|
||||
|
||||
```c
|
||||
struct gui_input input = {0};
|
||||
while (1) {
|
||||
gui_input_begin(&input);
|
||||
if (/*mouse moved*/)
|
||||
gui_input_motion(&input, mouse.x, mouse.y);
|
||||
if (/*key pressed*/)
|
||||
gui_input_key(&input, key, gui_true);
|
||||
if (/*key released*/)
|
||||
gui_input_key(&input, key, gui_false);
|
||||
if (/*mouse button pressed*/)
|
||||
gui_input_button(&input, mouse.x, mouse.y, gui_true);
|
||||
if (/*mouse button released */)
|
||||
gui_input_button(&input, mouse.x, mouse.y, gui_false);
|
||||
gui_input_end(&input);
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration
|
||||
The gui toolkit provides a number of different attributes that can be
|
||||
configured, like spacing, padding, size and color.
|
||||
While the widget API even expects you to provide the configuration
|
||||
for each and every widget the panel layer provides you with a set of
|
||||
attributes in the `gui_config` structure. The structure either needs to be
|
||||
filled by the user or can be setup with some default values by the function
|
||||
`gui_config_default`. Modification on the fly to the `gui_config` struct is in
|
||||
true immediate mode fashion possible and supported.
|
||||
|
||||
```c
|
||||
struct gui_style {
|
||||
gui_float rounding[GUI_ROUNDING_MAX];
|
||||
struct gui_vec2 properties[GUI_PROPERTY_MAX];
|
||||
struct gui_color colors[GUI_COLOR_COUNT];
|
||||
};
|
||||
```
|
||||
In addition to modifing the `gui_style` struct directly the styleration API
|
||||
enables you to temporarily change a property or color and revert back directly
|
||||
after the change is no longer needed. The number of temporary changes are
|
||||
limited but can be changed with constants `GUI_MAX_COLOR_STACK` and
|
||||
`GUI_MAX_ATTRIB_STACK`.
|
||||
|
||||
|
||||
```c
|
||||
gui_style_push_color(style, GUI_COLORS_PANEL, 255, 0, 0, 255);
|
||||
gui_style_push_attribute(style, GUI_ATTRIBUTE_PADDING, 10.0f, 5.0f);
|
||||
/* use the styleuration data */
|
||||
gui_style_pop_attribute(style);
|
||||
gui_style_pop_color(style);
|
||||
```
|
||||
|
||||
Since there is no direct font implementation in the toolkit but font handling is
|
||||
still an aspect of a gui implementation, the `gui_font` struct was introduced. It only
|
||||
contains the bare minimum of what is needed for font handling.
|
||||
For widgets the `gui_font` data has to be persistent while the
|
||||
panel hold the font internally. Important to node is that the font does not hold
|
||||
your font data but merely references it so you have to make sure that the font
|
||||
always points to a valid object.
|
||||
|
||||
```c
|
||||
struct gui_font {
|
||||
gui_handle userdata;
|
||||
gui_float height;
|
||||
gui_text_width_f width;
|
||||
};
|
||||
|
||||
font.userdata.ptr = your_font_data;
|
||||
font.height = your_font_data.height;
|
||||
font.width = your_font_string_width_callback_function;
|
||||
```
|
||||
|
||||
### Memory
|
||||
Almost all memory as well as object management for the toolkit
|
||||
is left to the user for maximum control. In fact a big subset of the toolkit can
|
||||
be used without any heap allocation at all. The only place where heap allocation
|
||||
is needed at all is for buffering draw calls. While the standart way of
|
||||
memory allocation in that case for libraries is to just provide allocator callbacks
|
||||
which is implemented aswell with the `gui_allocator`
|
||||
structure, there are two addition ways to provided memory. The
|
||||
first one is to just providing a static fixed size memory block to fill up which
|
||||
is handy for UIs with roughly known memory requirements. The other way of memory
|
||||
managment is to extend the fixed size block with the abiltiy to resize your block
|
||||
at the end of the frame if there is not enough memory.
|
||||
For the purpose of resizable fixed size memory blocks and for general
|
||||
information about memory consumption the `gui_memory_info` structure was
|
||||
added. It contains information about the allocated amount of data in the current
|
||||
frame as well as the needed amount if not enough memory was provided.
|
||||
|
||||
```c
|
||||
/* fixed size queue */
|
||||
void *memory = malloc(size);
|
||||
gui_command_queue queue;
|
||||
gui_command_queue_init_fixed(&queue, memory, MEMORY_SIZE, GUI_CLIP);
|
||||
```
|
||||
|
||||
```c
|
||||
/* dynamically growing queue */
|
||||
struct gui_allocator alloc;
|
||||
alloc.userdata = your_allocator;
|
||||
alloc.alloc = your_allocation_callback;
|
||||
alloc.relloac = your_reallocation_callback;
|
||||
alloc.free = your_free_callback;
|
||||
|
||||
struct gui_command_queue queue;
|
||||
const gui_size initial_size = 4*1024;
|
||||
const gui_float grow_factor = 2.0f;
|
||||
gui_command_queue_init(&queue, &alloc, initial_size, grow_factor);
|
||||
```
|
||||
|
||||
## FAQ
|
||||
#### Where is the demo/example code?
|
||||
The demo and example code can be found in the demo folder.
|
||||
@ -261,7 +142,7 @@ platform (Xlib, Win32) itself already provides a solution.
|
||||
## References
|
||||
- [Tutorial from Jari Komppa about imgui libraries](http://www.johno.se/book/imgui.html)
|
||||
- [Johannes 'johno' Norneby's article](http://iki.fi/sol/imgui/)
|
||||
- [ImGui: The inspiration for this project](https://github.com/ocornut/imgui)
|
||||
- [ImGui: The inspiration for this project from ocornut](https://github.com/ocornut/imgui)
|
||||
- [Nvidia's imgui toolkit](https://code.google.com/p/nvidia-widgets/)
|
||||
|
||||
# License
|
||||
|
28
demo/demo.c
28
demo/demo.c
@ -87,7 +87,6 @@ enum menu_edit_items {
|
||||
#undef ITEM
|
||||
MENU_EDIT_MAX
|
||||
};
|
||||
|
||||
static const char *weapons[] = {
|
||||
#define WEAPON(id,name) name,
|
||||
WEAPON_MAP(WEAPON)
|
||||
@ -296,7 +295,7 @@ color_picker(struct gui_context *panel, struct color_picker* control,
|
||||
*iter = (gui_byte)t;
|
||||
}
|
||||
|
||||
gui_layout_row_dynamic(&popup, 30, 3);
|
||||
gui_layout_row_dynamic(&popup, 30, 4);
|
||||
gui_spacing(&popup, 1);
|
||||
if (gui_button_text(&popup, "ok", GUI_BUTTON_DEFAULT)) {
|
||||
gui_popup_close(&popup);
|
||||
@ -540,15 +539,13 @@ widget_panel(struct gui_context *panel, struct state *demo)
|
||||
|
||||
{
|
||||
/* tiled widgets layout */
|
||||
gui_uint i = 0;
|
||||
struct gui_tiled_layout tiled;
|
||||
const char *items[] = {"item0", "item1", "item2", "item3"};
|
||||
enum gui_layout_format fmt = (demo->scaleable) ? GUI_DYNAMIC : GUI_STATIC;
|
||||
|
||||
/* setup tiled layout */
|
||||
gui_tiled_begin(&tiled, fmt, 300, 150);
|
||||
gui_tiled_begin(&tiled, fmt, 250, 150);
|
||||
if (!demo->scaleable) {
|
||||
gui_tiled_slot(&tiled, GUI_SLOT_LEFT, 150, GUI_SLOT_VERTICAL, 4);
|
||||
gui_tiled_slot(&tiled, GUI_SLOT_LEFT, 100, GUI_SLOT_VERTICAL, 4);
|
||||
gui_tiled_slot(&tiled, GUI_SLOT_RIGHT, 150, GUI_SLOT_VERTICAL, 4);
|
||||
} else {
|
||||
gui_tiled_slot(&tiled, GUI_SLOT_LEFT, 0.50, GUI_SLOT_VERTICAL, 4);
|
||||
@ -559,20 +556,22 @@ widget_panel(struct gui_context *panel, struct state *demo)
|
||||
/* setup widgets with tiled layout */
|
||||
gui_layout_row_tiled_begin(panel, &tiled);
|
||||
{
|
||||
gui_uint i = 0;
|
||||
gui_layout_row_tiled_push(panel, GUI_SLOT_LEFT, 1);
|
||||
gui_label(panel, "Test0", GUI_TEXT_CENTERED);
|
||||
gui_layout_row_tiled_push(panel, GUI_SLOT_LEFT, 2);
|
||||
gui_label(panel, "Test1", GUI_TEXT_CENTERED);
|
||||
for (i = 0; i < 4; ++i) {
|
||||
gui_layout_row_tiled_push(panel, GUI_SLOT_LEFT, i);
|
||||
const char *items[] = {"item0", "item1", "item2", "item3"};
|
||||
gui_layout_row_tiled_push(panel, GUI_SLOT_RIGHT, i);
|
||||
demo->list[i] = gui_button_toggle(panel, items[i], demo->list[i]);
|
||||
}
|
||||
gui_layout_row_tiled_push(panel, GUI_SLOT_RIGHT, 1);
|
||||
gui_label(panel, "Test0", GUI_TEXT_CENTERED);
|
||||
gui_layout_row_tiled_push(panel, GUI_SLOT_RIGHT, 2);
|
||||
gui_label(panel, "Test1", GUI_TEXT_CENTERED);
|
||||
}
|
||||
gui_layout_row_tiled_end(panel);
|
||||
}
|
||||
|
||||
/* item selection */
|
||||
if (!demo->scaleable) gui_layout_row_static(panel, 30, 30, 1);
|
||||
if (!demo->scaleable) gui_layout_row_static(panel, 30, 150, 1);
|
||||
else gui_layout_row_dynamic(panel, 30, 1);
|
||||
demo->spinner = gui_spinner(panel, 0, demo->spinner, 250, 10, &demo->spinner_active);
|
||||
|
||||
@ -585,7 +584,7 @@ widget_panel(struct gui_context *panel, struct state *demo)
|
||||
check_combo_box(panel, demo->check_values, LEN(demo->check_values), &demo->checkcom);
|
||||
|
||||
{
|
||||
/* immediate mode custom row layout */
|
||||
/* custom row layout by im */
|
||||
enum gui_layout_format fmt = (demo->scaleable) ? GUI_DYNAMIC : GUI_STATIC;
|
||||
gui_layout_row_begin(panel, fmt, 30, 2);
|
||||
{
|
||||
@ -830,7 +829,8 @@ run_demo(struct demo_gui *gui)
|
||||
}
|
||||
|
||||
/* popup panel */
|
||||
if (state->popup) {
|
||||
if (state->popup)
|
||||
{
|
||||
gui_popup_begin(&layout, &tab, GUI_POPUP_STATIC, 0, gui_rect(20, 100, 220, 150), gui_vec2(0,0));
|
||||
{
|
||||
if (gui_header(&tab, "Popup", GUI_CLOSEABLE, GUI_CLOSEABLE, GUI_HEADER_LEFT)) {
|
||||
|
@ -275,7 +275,7 @@ main(int argc, char *argv[])
|
||||
/* GUI */
|
||||
memset(&gui, 0, sizeof gui);
|
||||
gui_buffer_init_fixed(&gui.memory, calloc(MAX_MEMORY, 1), MAX_MEMORY);
|
||||
gui.font.userdata.ptr = vg;
|
||||
gui.font.userdata = gui_handle_ptr(vg);
|
||||
gui.font.width = font_get_width;
|
||||
nvgTextMetrics(vg, NULL, NULL, &gui.font.height);
|
||||
init_demo(&gui);
|
||||
@ -304,7 +304,7 @@ main(int argc, char *argv[])
|
||||
run_demo(&gui);
|
||||
|
||||
/* Draw */
|
||||
glClearColor(0.8588f, 0.835f, 0.86f, 1.0f);
|
||||
glClearColor(0.4f, 0.4f, 0.4f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
|
||||
draw(vg, &gui.queue, width, height);
|
||||
SDL_GL_SwapWindow(win);
|
||||
|
422
demo/opengl.c
422
demo/opengl.c
@ -17,14 +17,8 @@
|
||||
#include <GL/glu.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_GLYPH_H
|
||||
|
||||
/* macros */
|
||||
#define DTIME 17
|
||||
#define FONT_ATLAS_DEPTH 4
|
||||
|
||||
#define MAX_DRAW_COMMAND_MEMORY (4 * 1024)
|
||||
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
@ -72,286 +66,6 @@ file_load(const char* path, size_t* siz)
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* ==============================================================
|
||||
*
|
||||
* Font
|
||||
*
|
||||
* ===============================================================*/
|
||||
struct texCoord {
|
||||
float u;
|
||||
float v;
|
||||
};
|
||||
|
||||
enum font_atlas_dimension {
|
||||
FONT_ATLAS_DIM_64 = 64,
|
||||
FONT_ATLAS_DIM_128 = 128,
|
||||
FONT_ATLAS_DIM_256 = 256,
|
||||
FONT_ATLAS_DIM_512 = 512,
|
||||
FONT_ATLAS_DIM_1024 = 1024,
|
||||
FONT_ATLAS_DIM_2048 = 2048
|
||||
};
|
||||
|
||||
struct font_atlas {
|
||||
enum font_atlas_dimension dim;
|
||||
gui_size range;
|
||||
gui_size size;
|
||||
gui_byte *memory;
|
||||
};
|
||||
|
||||
struct font_glyph {
|
||||
unsigned int code;
|
||||
float xadvance;
|
||||
short width, height;
|
||||
float xoff, yoff;
|
||||
struct texCoord uv[2];
|
||||
};
|
||||
|
||||
struct font {
|
||||
float height;
|
||||
float scale;
|
||||
GLuint texture;
|
||||
unsigned int glyph_count;
|
||||
struct font_glyph *glyphes;
|
||||
const struct font_glyph *fallback;
|
||||
};
|
||||
|
||||
static void
|
||||
font_load_glyph(unsigned int code, struct font_glyph *glyph, FT_GlyphSlot slot)
|
||||
{
|
||||
glyph->code = code;
|
||||
glyph->width = (short)slot->bitmap.width;
|
||||
glyph->height = (short)slot->bitmap.rows;
|
||||
glyph->xoff = (float)slot->bitmap_left;
|
||||
glyph->yoff = (float)slot->bitmap_top;
|
||||
glyph->xadvance = (float)(slot->advance.x >> 6);
|
||||
}
|
||||
|
||||
static void
|
||||
font_load_glyphes(FT_Face face, struct font *font, size_t range)
|
||||
{
|
||||
size_t i;
|
||||
int ft_err;
|
||||
for (i = 0; i < range; ++i) {
|
||||
unsigned int index = FT_Get_Char_Index(face, i);
|
||||
if (!index) continue;
|
||||
ft_err = FT_Load_Glyph(face, index, 0);
|
||||
if (ft_err) continue;
|
||||
ft_err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
|
||||
if (ft_err) continue;
|
||||
font_load_glyph(index, &font->glyphes[i], face->glyph);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
font_pack_glyphes(struct font *font, float width, float height, size_t range)
|
||||
{
|
||||
size_t i;
|
||||
float xoff = 0, yoff = 0;
|
||||
float max_height = 0.0f;
|
||||
for (i = 0; i < range; ++i) {
|
||||
struct font_glyph *glyph = &font->glyphes[i];
|
||||
if ((xoff + glyph->width) > width) {
|
||||
yoff += max_height;
|
||||
max_height = 0.0f;
|
||||
xoff = 0.0f;
|
||||
}
|
||||
|
||||
glyph->uv[0].u = xoff / width;
|
||||
glyph->uv[0].v = yoff / height;
|
||||
glyph->uv[1].u = (xoff + glyph->width) / width;
|
||||
glyph->uv[1].v = (yoff + glyph->height) / height;
|
||||
if (glyph->height > max_height)
|
||||
max_height = glyph->height;
|
||||
xoff += glyph->width;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
font_atlas_blit(struct font_atlas *atlas, FT_GlyphSlot glyph,
|
||||
size_t off_x, size_t off_y)
|
||||
{
|
||||
size_t y, x;
|
||||
size_t width = glyph->bitmap.width;
|
||||
size_t height = glyph->bitmap.rows;
|
||||
const size_t pitch = atlas->dim * FONT_ATLAS_DEPTH;
|
||||
for (y = 0; y < height; y++) {
|
||||
size_t x_off = off_x * FONT_ATLAS_DEPTH;
|
||||
size_t y_off = (off_y + y) * pitch;
|
||||
unsigned char *dst = &atlas->memory[y_off + x_off];
|
||||
for (x = 0; x < width; ++x) {
|
||||
dst[0] = 255;
|
||||
dst[1] = 255;
|
||||
dst[2] = 255;
|
||||
dst[3] = glyph->bitmap.buffer[y * width + x];
|
||||
dst += FONT_ATLAS_DEPTH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
font_bake_glyphes(FT_Face face, struct font_atlas *atlas,
|
||||
const struct font *font)
|
||||
{
|
||||
size_t i;
|
||||
int ft_err;
|
||||
for (i = 0; i < atlas->range; ++i) {
|
||||
size_t x, y;
|
||||
struct font_glyph *glyph = &font->glyphes[i];
|
||||
unsigned int index = FT_Get_Char_Index(face, i);
|
||||
|
||||
if (!index) continue;
|
||||
ft_err = FT_Load_Glyph(face, index, 0);
|
||||
if (ft_err) continue;
|
||||
ft_err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
|
||||
if (ft_err) continue;
|
||||
|
||||
x = (gui_size)(glyph->uv[0].u * (gui_float)atlas->dim);
|
||||
y = (gui_size)(glyph->uv[0].v * (gui_float)atlas->dim);
|
||||
font_atlas_blit(atlas, face->glyph, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
font_load(struct font *font, struct font_atlas *atlas, unsigned int height,
|
||||
const unsigned char *data, size_t size)
|
||||
{
|
||||
int ret = 1;
|
||||
FT_Library ft_lib;
|
||||
FT_Face ft_face;
|
||||
|
||||
assert(font);
|
||||
assert(atlas);
|
||||
assert(font->glyphes);
|
||||
assert(atlas->memory);
|
||||
|
||||
if (!font || !atlas)
|
||||
return gui_false;
|
||||
if (!font->glyphes || !atlas->memory)
|
||||
return gui_false;
|
||||
if (FT_Init_FreeType(&ft_lib))
|
||||
return gui_false;
|
||||
if (FT_New_Memory_Face(ft_lib, data, (FT_Long)size, 0, &ft_face))
|
||||
goto cleanup;
|
||||
if (FT_Select_Charmap(ft_face, FT_ENCODING_UNICODE))
|
||||
goto failed;
|
||||
if (FT_Set_Char_Size(ft_face, height << 6, height << 6, 96, 96))
|
||||
goto failed;
|
||||
|
||||
font_load_glyphes(ft_face, font, atlas->range);
|
||||
font_pack_glyphes(font, atlas->dim, atlas->dim, atlas->range);
|
||||
font_bake_glyphes(ft_face, atlas, font);
|
||||
|
||||
failed:
|
||||
FT_Done_Face(ft_face);
|
||||
cleanup:
|
||||
FT_Done_FreeType(ft_lib);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gui_size
|
||||
font_get_text_width(gui_handle handle, const gui_char *t, gui_size l)
|
||||
{
|
||||
long unicode;
|
||||
size_t text_width = 0;
|
||||
const struct font_glyph *glyph;
|
||||
size_t text_len = 0;
|
||||
size_t glyph_len;
|
||||
struct font *font = (struct font*)handle.ptr;
|
||||
assert(font);
|
||||
if (!t || !l) return 0;
|
||||
|
||||
glyph_len = gui_utf_decode(t, &unicode, l);
|
||||
while (text_len < l && glyph_len) {
|
||||
if (unicode == GUI_UTF_INVALID) return 0;
|
||||
glyph = (unicode < font->glyph_count) ? &font->glyphes[unicode] : font->fallback;
|
||||
glyph = (glyph->code == 0) ? font->fallback : glyph;
|
||||
|
||||
text_len += glyph_len;
|
||||
text_width += (gui_size)((float)glyph->xadvance * font->scale);
|
||||
glyph_len = gui_utf_decode(t + text_len, &unicode, l - text_len);
|
||||
}
|
||||
return text_width;
|
||||
}
|
||||
|
||||
static void
|
||||
font_query_font_glyph(gui_handle handle, struct gui_user_font_glyph *glyph,
|
||||
gui_long unicode, gui_long next_codepoint)
|
||||
{
|
||||
const struct font_glyph *g;
|
||||
const struct font *font = (struct font*)handle.ptr;
|
||||
UNUSED(next_codepoint);
|
||||
if (unicode == GUI_UTF_INVALID) return;
|
||||
g = (unicode < font->glyph_count) ?
|
||||
&font->glyphes[unicode] :
|
||||
font->fallback;
|
||||
g = (g->code == 0) ? font->fallback : g;
|
||||
|
||||
glyph->offset.x = g->xoff * font->scale;
|
||||
glyph->offset.y = g->yoff * font->scale;
|
||||
glyph->width = g->width * font->scale;
|
||||
glyph->height = g->height * font->scale;
|
||||
glyph->xadvance = g->xadvance * font->scale;
|
||||
glyph->uv[0] = gui_vec2(g->uv[0].u, g->uv[0].v);
|
||||
glyph->uv[1] = gui_vec2(g->uv[1].u, g->uv[1].v);
|
||||
}
|
||||
|
||||
static void
|
||||
font_del(struct font *font)
|
||||
{
|
||||
glDeleteTextures(1, &font->texture);
|
||||
free(font->glyphes);
|
||||
free(font);
|
||||
}
|
||||
|
||||
static struct font*
|
||||
font_new(const char *path, unsigned int font_height, unsigned int bake_height,
|
||||
size_t range, enum font_atlas_dimension dim)
|
||||
{
|
||||
gui_byte *ttf_blob;
|
||||
gui_size ttf_blob_size;
|
||||
struct font_atlas atlas;
|
||||
struct font *font = (struct font*)calloc(sizeof(struct font), 1);
|
||||
|
||||
atlas.dim = dim;
|
||||
atlas.range = range;
|
||||
atlas.size = atlas.dim * atlas.dim * FONT_ATLAS_DEPTH;
|
||||
atlas.memory = (gui_byte*)calloc((gui_size)atlas.size, 1);
|
||||
|
||||
font->glyph_count = (unsigned int)atlas.range;
|
||||
font->glyphes = (struct font_glyph*)calloc(atlas.range, sizeof(struct font_glyph));
|
||||
font->fallback = &font->glyphes['?'];
|
||||
font->scale = (float)font_height / (gui_float)bake_height;
|
||||
font->height = (float)font_height;
|
||||
|
||||
ttf_blob = (unsigned char*)file_load(path, &ttf_blob_size);
|
||||
if (!font_load(font, &atlas, bake_height, ttf_blob, ttf_blob_size))
|
||||
goto failed;
|
||||
|
||||
glGenTextures(1, &font->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, font->texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dim, dim, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, atlas.memory);
|
||||
|
||||
free(atlas.memory);
|
||||
free(ttf_blob);
|
||||
return font;
|
||||
|
||||
failed:
|
||||
free(atlas.memory);
|
||||
free(ttf_blob);
|
||||
font_del(font);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ==============================================================
|
||||
*
|
||||
* Draw
|
||||
*
|
||||
* ===============================================================*/
|
||||
struct device {
|
||||
GLuint vbo, vao, ebo;
|
||||
GLuint prog;
|
||||
@ -362,8 +76,7 @@ struct device {
|
||||
GLint attrib_col;
|
||||
GLint uniform_tex;
|
||||
GLint uniform_proj;
|
||||
GLuint null_tex;
|
||||
struct gui_draw_list draw_list;
|
||||
GLuint font_tex;
|
||||
struct gui_draw_null_texture null;
|
||||
struct gui_buffer cmds;
|
||||
};
|
||||
@ -466,24 +179,6 @@ device_init(struct device *dev)
|
||||
glerror();
|
||||
}
|
||||
|
||||
{
|
||||
/* create default white texture which is needed to draw primitives. Any
|
||||
texture with a white pixel would do but since I do not have one I have
|
||||
to create one. */
|
||||
void *mem = calloc(64*64*4, 1);
|
||||
memset(mem, 255, 64*64*4);
|
||||
glGenTextures(1, &dev->null_tex);
|
||||
glBindTexture(GL_TEXTURE_2D, dev->null_tex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, mem);
|
||||
free(mem);
|
||||
glerror();
|
||||
}
|
||||
dev->null.texture.id = (gui_int)dev->null_tex;
|
||||
dev->null.uv = gui_vec2(0.0f, 0.0f);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
@ -491,6 +186,92 @@ device_init(struct device *dev)
|
||||
glerror();
|
||||
}
|
||||
|
||||
static struct gui_user_font
|
||||
font_bake_and_upload(struct device *dev, struct gui_font *font,
|
||||
const char *path, unsigned int font_height, const gui_long *range)
|
||||
{
|
||||
gui_size glyph_count;
|
||||
gui_size img_width, img_height;
|
||||
struct gui_font_glyph *glyphes;
|
||||
struct gui_baked_font baked_font;
|
||||
struct gui_user_font user_font;
|
||||
struct gui_recti custom;
|
||||
|
||||
memset(&baked_font, 0, sizeof(baked_font));
|
||||
memset(&user_font, 0, sizeof(user_font));
|
||||
memset(&custom, 0, sizeof(custom));
|
||||
|
||||
{
|
||||
/* bake and upload font texture */
|
||||
void *img, *tmp;
|
||||
size_t ttf_size;
|
||||
gui_size tmp_size, img_size;
|
||||
const char *custom_data = "....";
|
||||
struct gui_font_config config;
|
||||
char *ttf_blob = file_load(path, &ttf_size);
|
||||
if (!ttf_blob)
|
||||
die("[Font]: %s is not a file or cannot be found!\n", path);
|
||||
|
||||
/* setup font configuration */
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.ttf_blob = ttf_blob;
|
||||
config.ttf_size = ttf_size;
|
||||
config.font = &baked_font;
|
||||
config.coord_type = GUI_COORD_UV;
|
||||
config.range = range;
|
||||
config.pixel_snap = gui_false;
|
||||
config.size = font_height;
|
||||
config.spacing = gui_vec2(0,0);
|
||||
config.oversample_h = 3;
|
||||
config.oversample_v = 1;
|
||||
|
||||
/* query needed amount of memory for the font baking process */
|
||||
gui_font_bake_memory(&tmp_size, &glyph_count, &config, 1);
|
||||
glyphes = (struct gui_font_glyph*)calloc(sizeof(struct gui_font_glyph), glyph_count);
|
||||
tmp = calloc(1, tmp_size);
|
||||
|
||||
/* pack all glyphes and return needed image width height and memory size*/
|
||||
custom.w = 2; custom.h = 2;
|
||||
if (!gui_font_bake_pack(&img_size, &img_width,&img_height,&custom,tmp,tmp_size,&config, 1))
|
||||
die("[Font]: failed to load font!\n");
|
||||
|
||||
/* bake all glyphes and custom white pixel into image */
|
||||
img = calloc(1, img_size);
|
||||
gui_font_bake(img, img_width, img_height, tmp, tmp_size, glyphes, glyph_count, &config, 1);
|
||||
gui_font_bake_custom_data(img, img_width, img_height, custom, custom_data, 2, 2, '.', 'X');
|
||||
{
|
||||
/* convert alpha8 image into rgba8 image */
|
||||
void *img_rgba = calloc(4, img_height * img_width);
|
||||
gui_font_bake_convert(img_rgba, (gui_ushort)img_width, (gui_ushort)img_height, img);
|
||||
free(img);
|
||||
img = img_rgba;
|
||||
}
|
||||
{
|
||||
/* upload baked font image */
|
||||
glGenTextures(1, &dev->font_tex);
|
||||
glBindTexture(GL_TEXTURE_2D, dev->font_tex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)img_width, (GLsizei)img_height, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, img);
|
||||
}
|
||||
free(ttf_blob);
|
||||
free(tmp);
|
||||
free(img);
|
||||
}
|
||||
|
||||
/* default white pixel in a texture which is needed to draw primitives */
|
||||
dev->null.texture.id = (gui_int)dev->font_tex;
|
||||
dev->null.uv = gui_vec2((custom.x + 0.5f)/(gui_float)img_width, (custom.y + 0.5f)/(gui_float)img_height);
|
||||
/* setup font with glyphes. IMPORTANT: the font only references the glyphes
|
||||
this was done to have the possibility to have multible fonts with one
|
||||
total glyph array. Not quite sure if it is a good thing since the
|
||||
glyphes have to be freed as well. */
|
||||
gui_font_init(font, font_height, '?', glyphes, &baked_font, dev->null.texture);
|
||||
user_font = gui_font_ref(font);
|
||||
return user_font;
|
||||
}
|
||||
|
||||
static void
|
||||
device_shutdown(struct device *dev)
|
||||
{
|
||||
@ -499,11 +280,15 @@ device_shutdown(struct device *dev)
|
||||
glDeleteShader(dev->vert_shdr);
|
||||
glDeleteShader(dev->frag_shdr);
|
||||
glDeleteProgram(dev->prog);
|
||||
glDeleteTextures(1, &dev->null_tex);
|
||||
glDeleteTextures(1, &dev->font_tex);
|
||||
glDeleteBuffers(1, &dev->vbo);
|
||||
glDeleteBuffers(1, &dev->ebo);
|
||||
}
|
||||
|
||||
/* this is stupid but needed for C89 since sinf and cosf do not exist in that library version */
|
||||
static gui_float fsin(gui_float f) {return (gui_float)sin(f);}
|
||||
static gui_float fcos(gui_float f) {return (gui_float)cos(f);}
|
||||
|
||||
static void
|
||||
device_draw(struct device *dev, struct gui_command_queue *queue, int width, int height)
|
||||
{
|
||||
@ -557,6 +342,7 @@ device_draw(struct device *dev, struct gui_command_queue *queue, int width, int
|
||||
glBindVertexArray(dev->vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, dev->vbo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo);
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, max_vertex_memory, NULL, GL_STREAM_DRAW);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_memory, NULL, GL_STREAM_DRAW);
|
||||
glerror();
|
||||
@ -569,7 +355,7 @@ device_draw(struct device *dev, struct gui_command_queue *queue, int width, int
|
||||
gui_buffer_init_fixed(&vbuf, vertexes, (gui_size)max_vertex_memory);
|
||||
gui_buffer_init_fixed(&ebuf, elements, (gui_size)max_element_memory);
|
||||
gui_draw_list_init(&draw_list, &dev->cmds, &vbuf, &ebuf,
|
||||
sinf, cosf, dev->null, GUI_ANTI_ALIASING_ON);
|
||||
fsin, fcos, dev->null, GUI_ANTI_ALIASING_ON);
|
||||
gui_draw_list_load(&draw_list, queue, 1.0f, 22);
|
||||
}
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
@ -595,17 +381,12 @@ device_draw(struct device *dev, struct gui_command_queue *queue, int width, int
|
||||
glUseProgram((GLuint)last_prog);
|
||||
glBindTexture(GL_TEXTURE_2D, (GLuint)last_tex);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_vbo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, (GLuint)last_ebo);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)last_ebo);
|
||||
glBindVertexArray((GLuint)last_vao);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glerror();
|
||||
}
|
||||
|
||||
/* ==============================================================
|
||||
*
|
||||
* APP
|
||||
*
|
||||
* ===============================================================*/
|
||||
static void
|
||||
key(struct gui_input *in, SDL_Event *evt, gui_bool down)
|
||||
{
|
||||
@ -675,7 +456,6 @@ main(int argc, char *argv[])
|
||||
const char *font_path;
|
||||
SDL_Window *win;
|
||||
SDL_GLContext glContext;
|
||||
struct font *glfont;
|
||||
int win_width, win_height;
|
||||
unsigned int started;
|
||||
unsigned int dt;
|
||||
@ -684,6 +464,8 @@ main(int argc, char *argv[])
|
||||
/* GUI */
|
||||
struct device device;
|
||||
struct demo_gui gui;
|
||||
struct gui_font font;
|
||||
|
||||
font_path = argv[1];
|
||||
if (argc < 2)
|
||||
die("Missing TTF Font file argument!");
|
||||
@ -699,7 +481,6 @@ main(int argc, char *argv[])
|
||||
|
||||
/* OpenGL */
|
||||
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
glfont = font_new(font_path, 10, 10, 255, FONT_ATLAS_DIM_256);
|
||||
glewExperimental = 1;
|
||||
if (glewInit() != GLEW_OK)
|
||||
die("Failed to setup GLEW\n");
|
||||
@ -707,13 +488,10 @@ main(int argc, char *argv[])
|
||||
/* GUI */
|
||||
memset(&gui, 0, sizeof gui);
|
||||
gui_buffer_init_fixed(&gui.memory, calloc(MAX_MEMORY, 1), MAX_MEMORY);
|
||||
gui.font.userdata.ptr = glfont;
|
||||
gui.font.height = glfont->height;
|
||||
gui.font.width = font_get_text_width;
|
||||
gui.font.query = font_query_font_glyph;
|
||||
gui.font.texture.id = (gui_int)glfont->texture;
|
||||
mem = gui_buffer_alloc(&gui.memory, GUI_BUFFER_FRONT, MAX_DRAW_COMMAND_MEMORY, 0);
|
||||
gui_buffer_init_fixed(&device.cmds, mem, MAX_DRAW_COMMAND_MEMORY);
|
||||
gui.font = font_bake_and_upload(&device, &font, font_path, 14,
|
||||
gui_font_default_glyph_ranges());
|
||||
|
||||
init_demo(&gui);
|
||||
device_init(&device);
|
||||
@ -757,8 +535,8 @@ main(int argc, char *argv[])
|
||||
|
||||
cleanup:
|
||||
/* Cleanup */
|
||||
free(font.glyphes);
|
||||
free(gui_buffer_memory(&gui.memory));
|
||||
font_del(glfont);
|
||||
device_shutdown(&device);
|
||||
SDL_GL_DeleteContext(glContext);
|
||||
SDL_DestroyWindow(win);
|
||||
|
@ -468,7 +468,7 @@ main(int argc, char *argv[])
|
||||
/* GUI */
|
||||
memset(&gui, 0, sizeof gui);
|
||||
gui_buffer_init_fixed(&gui.memory, calloc(MAX_MEMORY, 1), MAX_MEMORY);
|
||||
gui.font.userdata.ptr = xw.font;
|
||||
gui.font.userdata = gui_handle_ptr(xw.font);
|
||||
gui.font.height = (gui_float)xw.font->height;
|
||||
gui.font.width = font_get_text_width;
|
||||
init_demo(&gui);
|
||||
|
638
gui.c
638
gui.c
@ -23,6 +23,7 @@
|
||||
#define GUI_UTF_INVALID 0xFFFD
|
||||
#define GUI_MAX_NUMBER_BUFFER 64
|
||||
|
||||
#define GUI_UNUSED(x) ((void)(x))
|
||||
#define GUI_LERP(a, b, t) ((a) + ((b) - (a)) * (t))
|
||||
#define GUI_SATURATE(x) (MAX(0, MIN(1.0f, x)))
|
||||
#define GUI_LEN(a) (sizeof(a)/sizeof(a)[0])
|
||||
@ -75,6 +76,19 @@ static const long gui_utfmax[GUI_UTF_SIZE+1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0
|
||||
*
|
||||
* ===============================================================
|
||||
*/
|
||||
static gui_uint
|
||||
gui_round_up_pow2(gui_uint v)
|
||||
{
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v++;
|
||||
return v;
|
||||
}
|
||||
|
||||
static gui_float
|
||||
gui_inv_sqrt(gui_float number)
|
||||
{
|
||||
@ -164,6 +178,22 @@ gui_rgb(gui_byte r, gui_byte g, gui_byte b)
|
||||
return ret;
|
||||
}
|
||||
|
||||
gui_handle
|
||||
gui_handle_ptr(void *ptr)
|
||||
{
|
||||
gui_handle handle;
|
||||
handle.ptr = ptr;
|
||||
return handle;
|
||||
}
|
||||
|
||||
gui_handle
|
||||
gui_handle_id(gui_int id)
|
||||
{
|
||||
gui_handle handle;
|
||||
handle.id = id;
|
||||
return handle;
|
||||
}
|
||||
|
||||
struct gui_image
|
||||
gui_subimage_ptr(void *ptr, gui_ushort w, gui_ushort h, struct gui_rect r)
|
||||
{
|
||||
@ -1706,6 +1736,7 @@ gui_draw_list_push_image(struct gui_draw_list *list, gui_handle texture)
|
||||
gui_draw_list_push_command(list, gui_null_rect, list->null.texture);
|
||||
} else {
|
||||
struct gui_draw_command *prev = gui_draw_list_command_last(list);
|
||||
if (prev->texture.id != texture.id)
|
||||
gui_draw_list_push_command(list, prev->clip_rect, texture);
|
||||
}
|
||||
}
|
||||
@ -2268,6 +2299,10 @@ gui_draw_list_add_text(struct gui_draw_list *list, const struct gui_user_font *f
|
||||
struct gui_user_font_glyph g;
|
||||
GUI_ASSERT(list);
|
||||
if (!list || !len || !text) return;
|
||||
if (rect.x > (list->clip_rect.x + list->clip_rect.w) ||
|
||||
rect.y > (list->clip_rect.y + list->clip_rect.h) ||
|
||||
rect.x < list->clip_rect.x || rect.y < list->clip_rect.y)
|
||||
return;
|
||||
|
||||
/* draw text background */
|
||||
gui_draw_list_add_rect_filled(list, rect, bg, 0.0f);
|
||||
@ -2288,7 +2323,8 @@ gui_draw_list_add_text(struct gui_draw_list *list, const struct gui_user_font *f
|
||||
|
||||
/* calculate and draw glyph drawing rectangle and image */
|
||||
gx = x + g.offset.x;
|
||||
gy = rect.y + (font->height/2) + (rect.h/2) - g.offset.y;
|
||||
/*gy = rect.y + (font->height/2) + (rect.h/2) - g.offset.y;*/
|
||||
gy = rect.y + (rect.h/2) - (font->height/2) + g.offset.y;
|
||||
gw = g.width; gh = g.height;
|
||||
char_width = g.xadvance;
|
||||
gui_draw_list_push_rect_uv(list, gui_vec2(gx,gy), gui_vec2(gx + gw, gy+ gh),
|
||||
@ -2452,6 +2488,563 @@ gui_draw_list_path_stroke(struct gui_draw_list *list, struct gui_color color,
|
||||
gui_draw_list_path_clear(list);
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* ==============================================================
|
||||
*
|
||||
* Font
|
||||
*
|
||||
* ===============================================================
|
||||
*/
|
||||
#ifdef GUI_COMPILE_WITH_FONT
|
||||
|
||||
/* this is a bloody mess but 'fixing' both stb libraries would be a pain */
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion"
|
||||
#pragma clang diagnostic ignored "-Wfloat-equal"
|
||||
#pragma clang diagnostic ignored "-Wbad-function-cast"
|
||||
#pragma clang diagnostic ignored "-Wcast-qual"
|
||||
#pragma clang diagnostic ignored "-Wshadow"
|
||||
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||
#pragma clang diagnostic ignored "-Wdeclaration-after-statement"
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#elif defined(__GNUC__) || defined(__GNUG__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
#pragma GCC diagnostic ignored "-Wsign-conversion"
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wbad-function-cast"
|
||||
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
|
||||
#pragma GCC diagnostic ignored "-Wtype-limits"
|
||||
#pragma GCC diagnostic ignored "-Wswitch-default"
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#elif _MSC_VER
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4456)
|
||||
#endif
|
||||
|
||||
#ifndef GUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||
#define STBRP_STATIC
|
||||
#define STB_RECT_PACK_IMPLEMENTATION
|
||||
#endif
|
||||
#include "stb_rect_pack.h"
|
||||
|
||||
#ifndef GUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||
#define STBTT_STATIC
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#endif
|
||||
#include "stb_truetype.h"
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__) || defined(__GNUG__)
|
||||
#pragma GCC diagnostic pop
|
||||
#elif _MSC_VER
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
|
||||
struct gui_font_bake_data {
|
||||
stbtt_fontinfo info;
|
||||
stbrp_rect *rects;
|
||||
stbtt_pack_range *ranges;
|
||||
gui_uint range_count;
|
||||
};
|
||||
|
||||
struct gui_font_baker {
|
||||
stbtt_pack_context spc;
|
||||
struct gui_font_bake_data *build;
|
||||
stbtt_packedchar *packed_chars;
|
||||
stbrp_rect *rects;
|
||||
stbtt_pack_range *ranges;
|
||||
};
|
||||
|
||||
static const gui_size gui_rect_align = GUI_ALIGNOF(stbrp_rect);
|
||||
static const gui_size gui_range_align = GUI_ALIGNOF(stbtt_pack_range);
|
||||
static const gui_size gui_char_align = GUI_ALIGNOF(stbtt_packedchar);
|
||||
static const gui_size gui_build_align = GUI_ALIGNOF(struct gui_font_bake_data);
|
||||
static const gui_size gui_baker_align = GUI_ALIGNOF(struct gui_font_baker);
|
||||
|
||||
static gui_size
|
||||
gui_range_count(const gui_long *range)
|
||||
{
|
||||
const gui_long *iter = range;
|
||||
GUI_ASSERT(range);
|
||||
if (!range) return 0;
|
||||
while (*(iter++) != 0);
|
||||
return (iter == range) ? 0 : (gui_size)((iter - range)/2);
|
||||
}
|
||||
|
||||
static gui_size
|
||||
gui_range_glyph_count(const gui_long *range, gui_size count)
|
||||
{
|
||||
gui_size i = 0;
|
||||
gui_size total_glyphes = 0;
|
||||
for (i = 0; i < count; ++i) {
|
||||
gui_size diff;
|
||||
gui_long f = range[(i*2)+0];
|
||||
gui_long t = range[(i*2)+1];
|
||||
GUI_ASSERT(t >= f);
|
||||
diff = (gui_size)((t - f) + 1);
|
||||
total_glyphes += diff;
|
||||
}
|
||||
return total_glyphes;
|
||||
}
|
||||
|
||||
const gui_long*
|
||||
gui_font_default_glyph_ranges(void)
|
||||
{
|
||||
static const gui_long ranges[] = {0x0020, 0x00FF, 0};
|
||||
return ranges;
|
||||
}
|
||||
|
||||
const gui_long*
|
||||
gui_font_chinese_glyph_ranges(void)
|
||||
{
|
||||
static const gui_long ranges[] = {
|
||||
0x0020, 0x00FF,
|
||||
0x3000, 0x30FF,
|
||||
0x31F0, 0x31FF,
|
||||
0xFF00, 0xFFEF,
|
||||
0x4e00, 0x9FAF,
|
||||
0
|
||||
};
|
||||
return ranges;
|
||||
}
|
||||
|
||||
const gui_long*
|
||||
gui_font_cyrillic_glyph_ranges(void)
|
||||
{
|
||||
static const gui_long ranges[] = {
|
||||
0x0020, 0x00FF,
|
||||
0x0400, 0x052F,
|
||||
0x2DE0, 0x2DFF,
|
||||
0xA640, 0xA69F,
|
||||
0
|
||||
};
|
||||
return ranges;
|
||||
}
|
||||
|
||||
void gui_font_bake_memory(gui_size *temp, gui_size *glyph_count,
|
||||
struct gui_font_config *config, gui_size count)
|
||||
{
|
||||
gui_size i;
|
||||
gui_size range_count = 0;
|
||||
|
||||
GUI_ASSERT(config);
|
||||
GUI_ASSERT(glyph_count);
|
||||
if (!config) {
|
||||
*temp = 0;
|
||||
*glyph_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
*glyph_count = 0;
|
||||
if (!config->range)
|
||||
config->range = gui_font_default_glyph_ranges();
|
||||
for (i = 0; i < count; ++i) {
|
||||
range_count += gui_range_count(config[i].range);
|
||||
*glyph_count += gui_range_glyph_count(config[i].range, range_count);
|
||||
}
|
||||
|
||||
*temp = *glyph_count * sizeof(stbrp_rect);
|
||||
*temp += range_count * sizeof(stbtt_pack_range);
|
||||
*temp += *glyph_count * sizeof(stbtt_packedchar);
|
||||
*temp += count * sizeof(struct gui_font_bake_data);
|
||||
*temp += sizeof(struct gui_font_baker);
|
||||
*temp += gui_rect_align + gui_range_align + gui_char_align;
|
||||
*temp += gui_build_align + gui_baker_align;
|
||||
}
|
||||
|
||||
static struct gui_font_baker*
|
||||
gui_font_baker(void *memory, gui_size glyph_count, gui_size count)
|
||||
{
|
||||
struct gui_font_baker *baker;
|
||||
if (!memory) return 0;
|
||||
/* setup baker inside a memory block */
|
||||
baker = (struct gui_font_baker*)GUI_ALIGN_PTR(memory, gui_baker_align);
|
||||
baker->build = (struct gui_font_bake_data*)GUI_ALIGN_PTR((baker + 1), gui_build_align);
|
||||
baker->packed_chars = (stbtt_packedchar*)GUI_ALIGN_PTR((baker->build + count), gui_char_align);
|
||||
baker->rects = (stbrp_rect*)GUI_ALIGN_PTR((baker->packed_chars + glyph_count), gui_rect_align);
|
||||
baker->ranges = (stbtt_pack_range*)GUI_ALIGN_PTR((baker->rects + glyph_count), gui_range_align);
|
||||
return baker;
|
||||
}
|
||||
|
||||
gui_bool
|
||||
gui_font_bake_pack(gui_size *image_memory, gui_size *width, gui_size *height,
|
||||
struct gui_recti *custom, void *temp, gui_size temp_size,
|
||||
const struct gui_font_config *config, gui_size count)
|
||||
{
|
||||
static const gui_size max_height = 1024 * 32;
|
||||
struct gui_font_baker* baker;
|
||||
gui_size total_glyph_count = 0;
|
||||
gui_size total_range_count = 0;
|
||||
gui_size i = 0;
|
||||
|
||||
GUI_ASSERT(image_memory);
|
||||
GUI_ASSERT(width);
|
||||
GUI_ASSERT(height);
|
||||
GUI_ASSERT(config);
|
||||
GUI_ASSERT(temp);
|
||||
GUI_ASSERT(temp_size);
|
||||
GUI_ASSERT(count);
|
||||
if (!image_memory || !width || !height || !config || !temp ||
|
||||
!temp_size || !count) return gui_false;
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
total_range_count += gui_range_count(config[i].range);
|
||||
total_glyph_count += gui_range_glyph_count(config[i].range, total_range_count);
|
||||
}
|
||||
|
||||
/* setup font baker from temporary memory */
|
||||
gui_zero(temp, temp_size);
|
||||
baker = gui_font_baker(temp, total_glyph_count, count);
|
||||
if (!baker) return gui_false;
|
||||
for (i = 0; i < count; ++i) {
|
||||
const struct gui_font_config *cfg = &config[i];
|
||||
if (!stbtt_InitFont(&baker->build[i].info, (const unsigned char*)cfg->ttf_blob, 0))
|
||||
return gui_false;
|
||||
}
|
||||
|
||||
*height = 0;
|
||||
*width = (total_glyph_count > 1000) ? 1024 : 512;
|
||||
stbtt_PackBegin(&baker->spc, NULL, (gui_int)*width, (int)max_height, 0, 1, 0);
|
||||
{
|
||||
gui_size input_i = 0;
|
||||
gui_size range_n = 0, rect_n = 0, char_n = 0;
|
||||
|
||||
/* pack custom user data first so it will be in the upper left corner */
|
||||
if (custom) {
|
||||
stbrp_rect custom_space;
|
||||
gui_zero(&custom_space, sizeof(custom_space));
|
||||
custom_space.w = (stbrp_coord)((custom->w * 2) + 1);
|
||||
custom_space.h = (stbrp_coord)(custom->h + 1);
|
||||
|
||||
stbtt_PackSetOversampling(&baker->spc, 1, 1);
|
||||
stbrp_pack_rects((stbrp_context*)baker->spc.pack_info, &custom_space, 1);
|
||||
*height = MAX(*height, (size_t)(custom_space.y + custom_space.h));
|
||||
|
||||
custom->x = custom_space.x;
|
||||
custom->y = custom_space.y;
|
||||
custom->w = custom_space.w;
|
||||
custom->h = custom_space.h;
|
||||
}
|
||||
|
||||
/* first font pass: pack all glyphes */
|
||||
for (input_i = 0; input_i < count; input_i++) {
|
||||
gui_size n = 0;
|
||||
const gui_long *in_range;
|
||||
const struct gui_font_config *cfg = &config[input_i];
|
||||
struct gui_font_bake_data *tmp = &baker->build[input_i];
|
||||
gui_size glyph_count, range_count;
|
||||
|
||||
/* count glyphes + ranges in current font */
|
||||
glyph_count = 0; range_count = 0;
|
||||
for (in_range = cfg->range; in_range[0] && in_range[1]; in_range += 2) {
|
||||
glyph_count += (gui_size)(in_range[1] - in_range[0]) + 1;
|
||||
range_count++;
|
||||
}
|
||||
|
||||
/* setup ranges */
|
||||
tmp->ranges = baker->ranges + range_n;
|
||||
tmp->range_count = (gui_uint)range_count;
|
||||
range_n += range_count;
|
||||
for (i = 0; i < range_count; ++i) {
|
||||
/*stbtt_pack_range *range = &tmp->ranges[i];*/
|
||||
in_range = &cfg->range[i * 2];
|
||||
tmp->ranges[i].font_size = cfg->size;
|
||||
tmp->ranges[i].first_unicode_codepoint_in_range = (gui_int)in_range[0];
|
||||
tmp->ranges[i].num_chars = (gui_int)(in_range[1]- in_range[0]) + 1;
|
||||
tmp->ranges[i].chardata_for_range = baker->packed_chars + char_n;
|
||||
char_n += (gui_size)tmp->ranges[i].num_chars;
|
||||
}
|
||||
|
||||
/* pack */
|
||||
tmp->rects = baker->rects + rect_n;
|
||||
rect_n += glyph_count;
|
||||
stbtt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v);
|
||||
n = (gui_size)stbtt_PackFontRangesGatherRects(&baker->spc, &tmp->info,
|
||||
tmp->ranges, (gui_int)tmp->range_count, tmp->rects);
|
||||
stbrp_pack_rects((stbrp_context*)baker->spc.pack_info, tmp->rects, (gui_int)n);
|
||||
|
||||
/* texture height */
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (tmp->rects[i].was_packed)
|
||||
*height = MAX(*height, (gui_size)(tmp->rects[i].y + tmp->rects[i].h));
|
||||
}
|
||||
}
|
||||
GUI_ASSERT(rect_n == total_glyph_count);
|
||||
GUI_ASSERT(char_n == total_glyph_count);
|
||||
GUI_ASSERT(range_n == total_range_count);
|
||||
}
|
||||
*height = gui_round_up_pow2((gui_uint)*height);
|
||||
*image_memory = (*width) * (*height);
|
||||
return gui_true;
|
||||
}
|
||||
|
||||
void
|
||||
gui_font_bake(void *image_memory, gui_size width, gui_size height,
|
||||
void *temp, gui_size temp_size, struct gui_font_glyph *glyphes,
|
||||
gui_size glyphes_count, const struct gui_font_config *config, gui_size font_count)
|
||||
{
|
||||
gui_size input_i = 0;
|
||||
struct gui_font_baker* baker;
|
||||
gui_long glyph_n = 0;
|
||||
|
||||
GUI_ASSERT(image_memory);
|
||||
GUI_ASSERT(width);
|
||||
GUI_ASSERT(height);
|
||||
GUI_ASSERT(config);
|
||||
GUI_ASSERT(temp);
|
||||
GUI_ASSERT(temp_size);
|
||||
GUI_ASSERT(font_count);
|
||||
GUI_ASSERT(glyphes_count);
|
||||
if (!image_memory || !width || !height || !config || !temp ||
|
||||
!temp_size || !font_count || !glyphes || !glyphes_count)
|
||||
return;
|
||||
|
||||
/* second font pass: render glyphes */
|
||||
baker = (struct gui_font_baker*)temp;
|
||||
gui_zero(image_memory, width * height);
|
||||
baker->spc.pixels = (unsigned char*)image_memory;
|
||||
baker->spc.height = (gui_int)height;
|
||||
for (input_i = 0; input_i < font_count; ++input_i) {
|
||||
const struct gui_font_config *cfg = &config[input_i];
|
||||
struct gui_font_bake_data *tmp = &baker->build[input_i];
|
||||
stbtt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v);
|
||||
stbtt_PackFontRangesRenderIntoRects(&baker->spc, &tmp->info, tmp->ranges,
|
||||
(gui_int)tmp->range_count, tmp->rects);
|
||||
}
|
||||
stbtt_PackEnd(&baker->spc);
|
||||
|
||||
/* third pass: setup font and glyphes */
|
||||
for (input_i = 0; input_i < font_count; ++input_i) {
|
||||
gui_size i = 0;
|
||||
gui_int char_idx = 0;
|
||||
gui_uint glyph_count = 0;
|
||||
const struct gui_font_config *cfg = &config[input_i];
|
||||
struct gui_font_bake_data *tmp = &baker->build[input_i];
|
||||
struct gui_baked_font *dst_font = cfg->font;
|
||||
|
||||
gui_float font_scale = stbtt_ScaleForPixelHeight(&tmp->info, cfg->size);
|
||||
gui_int unscaled_ascent, unscaled_descent, unscaled_line_gap;
|
||||
stbtt_GetFontVMetrics(&tmp->info, &unscaled_ascent, &unscaled_descent,
|
||||
&unscaled_line_gap);
|
||||
|
||||
/* fill baked font */
|
||||
dst_font->ranges = cfg->range;
|
||||
dst_font->height = cfg->size;
|
||||
dst_font->ascent = ((gui_float)unscaled_ascent * font_scale);
|
||||
dst_font->descent = ((gui_float)unscaled_descent * font_scale);
|
||||
dst_font->glyph_offset = glyph_n;
|
||||
|
||||
/* fill own baked font glyph array */
|
||||
for (i = 0; i < tmp->range_count; ++i) {
|
||||
stbtt_pack_range *range = &tmp->ranges[i];
|
||||
for (char_idx = 0; char_idx < range->num_chars; char_idx++) {
|
||||
gui_int codepoint = 0;
|
||||
gui_float dummy_x = 0, dummy_y = 0;
|
||||
stbtt_aligned_quad q;
|
||||
struct gui_font_glyph *glyph;
|
||||
|
||||
const stbtt_packedchar *pc = &range->chardata_for_range[char_idx];
|
||||
glyph_count++;
|
||||
if (!pc->x0 && !pc->x1 && !pc->y0 && !pc->y1) continue;
|
||||
codepoint = range->first_unicode_codepoint_in_range + char_idx;
|
||||
stbtt_GetPackedQuad(range->chardata_for_range, (gui_int)width,
|
||||
(gui_int)height, char_idx, &dummy_x, &dummy_y, &q, 0);
|
||||
|
||||
/* fill own glyph type with data */
|
||||
glyph = &glyphes[dst_font->glyph_offset + char_idx];
|
||||
glyph->codepoint = (gui_long)codepoint;
|
||||
glyph->x0 = q.x0; glyph->y0 = q.y0; glyph->x1 = q.x1; glyph->y1 = q.y1;
|
||||
glyph->y0 += (dst_font->ascent + 0.5f);
|
||||
glyph->y1 += (dst_font->ascent + 0.5f);
|
||||
if (cfg->coord_type == GUI_COORD_PIXEL) {
|
||||
glyph->u0 = q.s0 * (gui_float)width;
|
||||
glyph->v0 = q.t0 * (gui_float)height;
|
||||
glyph->u1 = q.s1 * (gui_float)width;
|
||||
glyph->v1 = q.t1 * (gui_float)height;
|
||||
} else {
|
||||
glyph->u0 = q.s0;
|
||||
glyph->v0 = q.t0;
|
||||
glyph->u1 = q.s1;
|
||||
glyph->v1 = q.t1;
|
||||
}
|
||||
glyph->xadvance = (pc->xadvance + cfg->spacing.x);
|
||||
if (cfg->pixel_snap)
|
||||
glyph->xadvance = (gui_float)(gui_int)(glyph->xadvance + 0.5f);
|
||||
}
|
||||
}
|
||||
dst_font->glyph_count = glyph_count;
|
||||
glyph_n += dst_font->glyph_count;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gui_font_bake_custom_data(void *img_memory, gui_size img_width, gui_size img_height,
|
||||
struct gui_recti img_dst, const char *texture_data_mask, gui_size tex_width,
|
||||
gui_size tex_height, gui_char white, gui_char black)
|
||||
{
|
||||
gui_byte *pixels;
|
||||
gui_size y = 0, x = 0, n = 0;
|
||||
GUI_ASSERT(img_memory);
|
||||
GUI_ASSERT(img_width);
|
||||
GUI_ASSERT(img_height);
|
||||
GUI_ASSERT(texture_data_mask);
|
||||
GUI_UNUSED(tex_height);
|
||||
if (!img_memory || !img_width || !img_height || !texture_data_mask)
|
||||
return;
|
||||
|
||||
pixels = (gui_byte*)img_memory;
|
||||
for (y = 0, n = 0; y < tex_height; ++y) {
|
||||
for (x = 0; x < tex_width; ++x, ++n) {
|
||||
const gui_size off0 = (img_dst.x + x) + (img_dst.y + y) * img_width;
|
||||
const gui_size off1 = off0 + 1 + tex_width;
|
||||
pixels[off0] = (texture_data_mask[n] == white) ? 0xFF : 0x00;
|
||||
pixels[off1] = (texture_data_mask[n] == black) ? 0xFF : 0x00;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gui_font_bake_convert(void *out_memory, gui_ushort img_width, gui_ushort img_height,
|
||||
const void *in_memory)
|
||||
{
|
||||
gui_int n = 0;
|
||||
const gui_byte *src;
|
||||
gui_uint *dst;
|
||||
GUI_ASSERT(out_memory);
|
||||
GUI_ASSERT(in_memory);
|
||||
GUI_ASSERT(img_width);
|
||||
GUI_ASSERT(img_height);
|
||||
if (!out_memory || !in_memory || !img_height || !img_width) return;
|
||||
|
||||
dst = (gui_uint*)out_memory;
|
||||
src = (const gui_byte*)in_memory;
|
||||
for (n = (gui_int)(img_width * img_height); n > 0; n--)
|
||||
*dst++ = ((gui_uint)(*src++) << 24) | 0x00FFFFFF;
|
||||
}
|
||||
/*
|
||||
* -------------------------------------------------------------
|
||||
*
|
||||
* Font
|
||||
*
|
||||
* --------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
gui_font_init(struct gui_font *font, gui_float pixel_height,
|
||||
gui_long fallback_codepoint, struct gui_font_glyph *glyphes,
|
||||
const struct gui_baked_font *baked_font, gui_handle atlas)
|
||||
{
|
||||
GUI_ASSERT(font);
|
||||
GUI_ASSERT(glyphes);
|
||||
GUI_ASSERT(baked_font);
|
||||
if (!font || !glyphes || !baked_font)
|
||||
return;
|
||||
|
||||
gui_zero(font, sizeof(*font));
|
||||
font->ascent = baked_font->ascent;
|
||||
font->descent = baked_font->descent;
|
||||
font->size = baked_font->height;
|
||||
font->scale = (gui_float)pixel_height / (gui_float)font->size;
|
||||
font->glyphes = &glyphes[baked_font->glyph_offset];
|
||||
font->glyph_count = baked_font->glyph_count;
|
||||
font->ranges = baked_font->ranges;
|
||||
font->atlas = atlas;
|
||||
font->fallback_codepoint = fallback_codepoint;
|
||||
font->fallback = gui_font_find_glyph(font, fallback_codepoint);
|
||||
}
|
||||
|
||||
const struct gui_font_glyph*
|
||||
gui_font_find_glyph(struct gui_font *font, gui_long unicode)
|
||||
{
|
||||
gui_size i = 0;
|
||||
gui_size count;
|
||||
gui_size total_glyphes = 0;
|
||||
const struct gui_font_glyph *glyph = 0;
|
||||
GUI_ASSERT(font);
|
||||
|
||||
glyph = font->fallback;
|
||||
count = gui_range_count(font->ranges);
|
||||
for (i = 0; i < count; ++i) {
|
||||
gui_size diff;
|
||||
gui_long f = font->ranges[(i*2)+0];
|
||||
gui_long t = font->ranges[(i*2)+1];
|
||||
diff = (gui_size)((t - f) + 1);
|
||||
if (unicode >= f && unicode <= t)
|
||||
return &font->glyphes[(total_glyphes + (gui_size)(unicode - f))];
|
||||
total_glyphes += diff;
|
||||
}
|
||||
return glyph;
|
||||
}
|
||||
|
||||
static gui_size
|
||||
gui_font_text_width(gui_handle handle, const gui_char *text, gui_size len)
|
||||
{
|
||||
gui_long unicode;
|
||||
const struct gui_font_glyph *glyph;
|
||||
struct gui_font *font;
|
||||
gui_size text_len = 0;
|
||||
gui_size text_width = 0;
|
||||
gui_size glyph_len;
|
||||
font = (struct gui_font*)handle.ptr;
|
||||
GUI_ASSERT(font);
|
||||
if (!font || !text || !len)
|
||||
return 0;
|
||||
|
||||
glyph_len = gui_utf_decode(text, &unicode, len);
|
||||
while (text_len < len && glyph_len) {
|
||||
if (unicode == GUI_UTF_INVALID) return 0;
|
||||
glyph = gui_font_find_glyph(font, unicode);
|
||||
text_len += glyph_len;
|
||||
text_width += (gui_size)((glyph->xadvance * font->scale));
|
||||
glyph_len = gui_utf_decode(text + text_len, &unicode, len - text_len);
|
||||
}
|
||||
return text_width;
|
||||
}
|
||||
|
||||
#if GUI_COMPILE_WITH_VERTEX_BUFFER
|
||||
static void
|
||||
gui_font_query_font_glyph(gui_handle handle, struct gui_user_font_glyph *glyph,
|
||||
gui_long codepoint, gui_long next_codepoint)
|
||||
{
|
||||
const struct gui_font_glyph *g;
|
||||
struct gui_font *font;
|
||||
GUI_ASSERT(glyph);
|
||||
GUI_UNUSED(next_codepoint);
|
||||
font = (struct gui_font*)handle.ptr;
|
||||
GUI_ASSERT(font);
|
||||
if (!font || !glyph)
|
||||
return;
|
||||
|
||||
g = gui_font_find_glyph(font, codepoint);
|
||||
glyph->width = (g->x1 - g->x0) * font->scale;
|
||||
glyph->height = (g->y1 - g->y0) * font->scale;
|
||||
glyph->offset = gui_vec2(g->x0 * font->scale, g->y0 * font->scale);
|
||||
glyph->xadvance = (g->xadvance * font->scale);
|
||||
glyph->uv[0] = gui_vec2(g->u0, g->v0);
|
||||
glyph->uv[1] = gui_vec2(g->u1, g->v1);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct gui_user_font
|
||||
gui_font_ref(struct gui_font *font)
|
||||
{
|
||||
struct gui_user_font user_font;
|
||||
gui_zero(&user_font, sizeof(user_font));
|
||||
user_font.height = font->size * font->scale;
|
||||
user_font.width = gui_font_text_width;
|
||||
user_font.userdata.ptr = font;
|
||||
#if GUI_COMPILE_WITH_VERTEX_BUFFER
|
||||
user_font.query = gui_font_query_font_glyph;
|
||||
user_font.texture = font->atlas;
|
||||
#endif
|
||||
return user_font;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* ==============================================================
|
||||
*
|
||||
@ -2488,7 +3081,6 @@ gui_edit_buffer_insert(gui_edit_buffer *buffer, gui_size pos,
|
||||
gui_edit_buffer_append(buffer, str, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
mem = gui_buffer_alloc(buffer, GUI_BUFFER_FRONT, len * sizeof(char), 0);
|
||||
if (!mem) return 0;
|
||||
|
||||
@ -4114,7 +4706,6 @@ void
|
||||
gui_tiled_slot_bounds(struct gui_rect *bounds,
|
||||
const struct gui_tiled_layout *layout, enum gui_tiled_layout_slot_index slot)
|
||||
{
|
||||
gui_float width, height;
|
||||
const struct gui_tiled_slot *s;
|
||||
GUI_ASSERT(layout);
|
||||
if (!layout) return;
|
||||
@ -4162,7 +4753,6 @@ gui_tiled_bounds(struct gui_rect *bounds, const struct gui_tiled_layout *layout,
|
||||
void
|
||||
gui_tiled_end(struct gui_tiled_layout *layout)
|
||||
{
|
||||
gui_float w;
|
||||
gui_float centerh, centerv;
|
||||
const struct gui_tiled_slot *top, *bottom;
|
||||
const struct gui_tiled_slot *left, *right;
|
||||
@ -4227,7 +4817,6 @@ gui_window_init(struct gui_window *window, struct gui_rect bounds,
|
||||
gui_command_queue_insert_back(queue, &window->buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gui_window_set_config(struct gui_window *panel, const struct gui_style *config)
|
||||
{
|
||||
@ -4327,11 +4916,11 @@ gui_window_is_minimized(struct gui_window *panel)
|
||||
{return panel->flags & GUI_WINDOW_MINIMIZED;}
|
||||
|
||||
/*
|
||||
* ==============================================================
|
||||
* -------------------------------------------------------------
|
||||
*
|
||||
* Context
|
||||
*
|
||||
* ===============================================================
|
||||
* --------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
gui_begin(struct gui_context *context, struct gui_window *window)
|
||||
@ -4493,7 +5082,6 @@ gui_begin_tiled(struct gui_context *context, struct gui_window *window,
|
||||
struct gui_tiled_layout *tiled, enum gui_tiled_layout_slot_index slot,
|
||||
gui_uint index)
|
||||
{
|
||||
struct gui_command_queue *queue;
|
||||
GUI_ASSERT(context);
|
||||
GUI_ASSERT(window);
|
||||
GUI_ASSERT(tiled);
|
||||
@ -5368,26 +5956,9 @@ gui_layout_row_tiled_begin(struct gui_context *layout,
|
||||
|
||||
layout->row.tiled = tiled;
|
||||
gui_panel_layout(layout, tiled->height, 2);
|
||||
if (tiled->fmt == GUI_STATIC) {
|
||||
/* calculate bounds of the tiled layout */
|
||||
struct gui_rect clip, space;
|
||||
space.x = layout->at_x;
|
||||
space.y = layout->at_y;
|
||||
space.w = layout->width;
|
||||
space.h = layout->row.height;
|
||||
|
||||
/* setup clipping rect for the free space to prevent overdraw */
|
||||
gui_unify(&clip, &layout->clip, space.x,space.y,space.x+space.w,space.y+space.h);
|
||||
gui_command_buffer_push_scissor(layout->buffer, clip);
|
||||
layout->row.clip = layout->clip;
|
||||
layout->clip = clip;
|
||||
layout->row.type = GUI_LAYOUT_STATIC_TILED;
|
||||
} else layout->row.type = GUI_LAYOUT_DYNAMIC_TILED;
|
||||
|
||||
layout->row.ratio = 0;
|
||||
layout->row.item_width = 0;
|
||||
layout->row.item_offset = 0;
|
||||
layout->row.filled = 0;
|
||||
layout->row.type = (tiled->fmt == GUI_STATIC) ?
|
||||
GUI_LAYOUT_STATIC_TILED:
|
||||
GUI_LAYOUT_DYNAMIC_TILED;
|
||||
}
|
||||
|
||||
void
|
||||
@ -5452,13 +6023,9 @@ gui_layout_row_tiled_end(struct gui_context *layout)
|
||||
if (!layout) return;
|
||||
if (!layout->valid) return;
|
||||
|
||||
layout->row.item_width = 0;
|
||||
layout->row.item_height = 0;
|
||||
layout->row.item_offset = 0;
|
||||
gui_zero(&layout->row.item, sizeof(layout->row.item));
|
||||
if (layout->row.tiled->fmt == GUI_STATIC)
|
||||
gui_command_buffer_push_scissor(layout->buffer, layout->clip);
|
||||
layout->row.tiled = 0;
|
||||
layout->row.columns = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -5517,6 +6084,7 @@ gui_panel_alloc_space(struct gui_rect *bounds, struct gui_context *layout)
|
||||
layout->row.filled += layout->row.item_width;
|
||||
layout->row.index = 0;
|
||||
} break;
|
||||
case GUI_LAYOUT_STATIC_TILED:
|
||||
case GUI_LAYOUT_DYNAMIC_TILED: {
|
||||
/* dynamic tiled layout widget placing */
|
||||
bounds->x = layout->at_x + layout->row.item.x + padding.x;
|
||||
@ -5569,9 +6137,6 @@ gui_panel_alloc_space(struct gui_rect *bounds, struct gui_context *layout)
|
||||
layout->row.item_offset += item_width + spacing.x;
|
||||
layout->row.index = 0;
|
||||
} break;
|
||||
case GUI_LAYOUT_STATIC_TILED:
|
||||
/* static tiled layout widget placing */
|
||||
layout->row.index = 0;
|
||||
case GUI_LAYOUT_STATIC_FREE: {
|
||||
/* free widget placing */
|
||||
bounds->x = layout->clip.x + layout->row.item.x;
|
||||
@ -6612,7 +7177,6 @@ gui_graph_callback(struct gui_context *layout, enum gui_graph_type type,
|
||||
gui_graph_end(layout, &graph);
|
||||
return index;
|
||||
}
|
||||
|
||||
/*
|
||||
* -------------------------------------------------------------
|
||||
*
|
||||
|
352
gui.h
352
gui.h
@ -54,10 +54,10 @@ extern "C" {
|
||||
and the module of the library will not be compiled */
|
||||
#define GUI_COMPILE_WITH_FONT 1
|
||||
/* setting this define to 1 adds the `stb_truetype` and `stb_rect_pack` header
|
||||
to this library and provided a default font for font loading and rendering.
|
||||
to this library and provides a default font for font loading and rendering.
|
||||
If you already have font handling or do not want to use this font handler
|
||||
you can just set this define to zero and the font module will not be compiled
|
||||
and the two header will not be needed. */
|
||||
and the two headers will not be needed. */
|
||||
/*
|
||||
* ==============================================================
|
||||
*
|
||||
@ -109,6 +109,7 @@ struct gui_color {gui_byte r,g,b,a;};
|
||||
struct gui_vec2 {gui_float x,y;};
|
||||
struct gui_vec2i {gui_short x, y;};
|
||||
struct gui_rect {gui_float x,y,w,h;};
|
||||
struct gui_recti {gui_ushort x,y,w,h;};
|
||||
typedef gui_char gui_glyph[GUI_UTF_SIZE];
|
||||
struct gui_key {gui_bool down; gui_uint clicked;};
|
||||
typedef union {void *ptr; gui_int id;} gui_handle;
|
||||
@ -120,12 +121,6 @@ enum gui_collapse_states {GUI_MINIMIZED = gui_false, GUI_MAXIMIZED = gui_true};
|
||||
struct gui_user_font;
|
||||
struct gui_edit_box;
|
||||
struct gui_user_font_glyph;
|
||||
typedef gui_bool(*gui_filter)(gui_long unicode);
|
||||
typedef void(*gui_paste_f)(gui_handle, struct gui_edit_box*);
|
||||
typedef void(*gui_copy_f)(gui_handle, const char*, gui_size size);
|
||||
typedef gui_size(*gui_text_width_f)(gui_handle, const gui_char*, gui_size);
|
||||
typedef void(*gui_query_font_glyph_f)(gui_handle, struct gui_user_font_glyph*,
|
||||
gui_long codepoint, gui_long next_codepoint);
|
||||
/*
|
||||
* ==============================================================
|
||||
*
|
||||
@ -167,6 +162,8 @@ struct gui_rect gui_get_null_rect(void);
|
||||
gui_size gui_utf_decode(const gui_char*, gui_long*, gui_size);
|
||||
gui_size gui_utf_encode(gui_long, gui_char*, gui_size);
|
||||
gui_size gui_utf_len(const gui_char*, gui_size len);
|
||||
gui_handle gui_handle_ptr(void*);
|
||||
gui_handle gui_handle_id(gui_int);
|
||||
struct gui_image gui_image_ptr(void*);
|
||||
struct gui_image gui_image_id(gui_int);
|
||||
struct gui_image gui_subimage_ptr(void*, gui_ushort w, gui_ushort h, struct gui_rect);
|
||||
@ -326,12 +323,14 @@ void gui_input_char(struct gui_input*, char);
|
||||
*/
|
||||
void gui_input_end(struct gui_input*);
|
||||
/* this function sets the input state to readable */
|
||||
gui_bool gui_input_has_mouse_click_in_rect(const struct gui_input*, enum gui_buttons, struct gui_rect);
|
||||
gui_bool gui_input_has_mouse_click_in_rect(const struct gui_input*,enum gui_buttons,
|
||||
struct gui_rect);
|
||||
/* this function returns true if a mouse click inside a rectangle occured in prev frames */
|
||||
gui_bool gui_input_has_mouse_click_down_in_rect(const struct gui_input*, enum gui_buttons,
|
||||
struct gui_rect, gui_bool down);
|
||||
/* this function returns true if a mouse down click inside a rectangle occured in prev frames */
|
||||
gui_bool gui_input_is_mouse_click_in_rect(const struct gui_input*, enum gui_buttons, struct gui_rect);
|
||||
gui_bool gui_input_is_mouse_click_in_rect(const struct gui_input*, enum gui_buttons,
|
||||
struct gui_rect);
|
||||
/* this function returns true if a mouse click inside a rectangle occured and was just clicked */
|
||||
gui_bool gui_input_is_mouse_prev_hovering_rect(const struct gui_input*, struct gui_rect);
|
||||
/* this function returns true if the mouse hovers over a rectangle */
|
||||
@ -814,8 +813,10 @@ const struct gui_command *gui_command_buffer_next(struct gui_command_buffer*,
|
||||
simplifies and reduces the amount of code needed with just using command buffers.
|
||||
|
||||
Internally the command queue has a list of command buffers which can be
|
||||
modified to create a certain sequence, for example the `gui_window_begin`
|
||||
function changes the list to create overlapping windows.
|
||||
modified to create a certain sequence, for example the `gui_begin`
|
||||
function changes the list to create overlapping windows, while `gui_begin_tiled`
|
||||
always draws all its windows in the background by pushing its buffers to the
|
||||
beginning of the list.
|
||||
|
||||
USAGE
|
||||
----------------------------
|
||||
@ -885,7 +886,7 @@ struct gui_command_queue {
|
||||
struct gui_command_buffer_list list;
|
||||
/* list of each memory buffer inside the queue */
|
||||
struct gui_command_sub_buffer_stack stack;
|
||||
/* subbuffer stack for overlapping child windows in windows */
|
||||
/* subbuffer stack for overlapping popup windows in windows */
|
||||
gui_bool build;
|
||||
/* flag indicating if a complete command list was build inside the queue*/
|
||||
};
|
||||
@ -1060,7 +1061,9 @@ struct gui_draw_vertex {
|
||||
|
||||
enum gui_draw_list_stroke {
|
||||
GUI_STROKE_OPEN = gui_false,
|
||||
/* build up path has no connection back to the beginning */
|
||||
GUI_STROKE_CLOSED = gui_true
|
||||
/* build up path has a connection back to the beginning */
|
||||
};
|
||||
|
||||
struct gui_draw_command {
|
||||
@ -1288,12 +1291,72 @@ void gui_draw_list_path_stroke(struct gui_draw_list*, struct gui_color,
|
||||
*
|
||||
* ===============================================================
|
||||
*/
|
||||
/* FONT
|
||||
----------------------------
|
||||
Font handling in this library can be achived in three different ways.
|
||||
The first and simplest ways is by just using your font handling mechanism
|
||||
and provide a simple callback for text string width calculation with
|
||||
`gui_user_font`. This requires the default drawing output
|
||||
and is not possible for the optional vertex buffer output.
|
||||
|
||||
The second way of font handling is by using the same `gui_user_font` struct
|
||||
to reference a font as before but providing a second callback for
|
||||
`gui_user_font_glyph` querying which is used for text drawing in the optional vertex
|
||||
buffer output. In addition to the callback it is also required to provide
|
||||
a texture atlas from the font to draw.
|
||||
|
||||
The final and most complex way is to use the optional font baker
|
||||
and font handling function, which requires two additional headers for
|
||||
TTF font baking. While the previous two methods did no need any functions
|
||||
outside callbacks and are therefore rather simple to handle, the final
|
||||
font handling method quite complex and you need to handle the complex
|
||||
font baking API. The reason why it is complex is because there are multible
|
||||
ways of using the API. For example it must be possible to use the font
|
||||
for default command output as well as vertex buffer output. So for example
|
||||
texture coordinates can either be UV for vertex buffer output or absolute pixel
|
||||
for drawing function based on pixels. Furthermore it is possible to incoperate
|
||||
custom user data into the resulting baked image (for example a white pixel for the
|
||||
vertex buffer output). In addition and probably the most complex aspect of
|
||||
the baking API was to incoperate baking of multible fonts into one image.
|
||||
|
||||
In general the font baking API can be understood as having a number of
|
||||
loaded in memory TTF-fonts, font baking configuration and optional custom
|
||||
render data as input, while the output is made of font specific data, a big
|
||||
glyph array of all baked glyphes and the baked image. The API
|
||||
was designed that way to have a typical file format and not
|
||||
a perfectly ready in memory library instance of a font. The reason is more
|
||||
control and seperates the font baking code from the in library used font format.
|
||||
|
||||
USAGE
|
||||
----------------------------
|
||||
font baking functions
|
||||
gui_font_bake_memory -- calculates the needed font baking memory
|
||||
gui_font_bake_pack -- packs all glyphes and calculates needed image memory
|
||||
gui_font_bake -- renders all glyphes inside an image and setups glyphes
|
||||
gui_font_bake_custom_data -- renders custom user data into the image
|
||||
gui_font_bake_convert -- converts the baked image from alpha8 to rgba8
|
||||
|
||||
font functions
|
||||
gui_font_init -- initilizes the font
|
||||
gui_font_ref -- create a user font out of the font
|
||||
gui_font_find_glyph -- finds and returns a glyph from the font
|
||||
*/
|
||||
typedef gui_size(*gui_text_width_f)(gui_handle, const gui_char*, gui_size);
|
||||
typedef void(*gui_query_font_glyph_f)(gui_handle, struct gui_user_font_glyph*,
|
||||
gui_long codepoint, gui_long next_codepoint);
|
||||
|
||||
#if GUI_COMPILE_WITH_VERTEX_BUFFER
|
||||
struct gui_user_font_glyph {
|
||||
struct gui_vec2 uv[2];
|
||||
/* texture coordinates */
|
||||
struct gui_vec2 offset;
|
||||
/* offset between top left and glyph */
|
||||
gui_float width, height;
|
||||
/* size of the glyph */
|
||||
gui_float xadvance;
|
||||
/* offset to the next glyph */
|
||||
};
|
||||
#endif
|
||||
|
||||
struct gui_user_font {
|
||||
gui_handle userdata;
|
||||
@ -1311,31 +1374,187 @@ struct gui_user_font {
|
||||
};
|
||||
|
||||
#ifdef GUI_COMPILE_WITH_FONT
|
||||
struct gui_font_atlas {
|
||||
gui_byte *pixels;
|
||||
gui_int tex_width;
|
||||
gui_int tex_height;
|
||||
struct gui_vec2 white_pixel;
|
||||
enum gui_font_coord_type {
|
||||
GUI_COORD_UV,
|
||||
/* texture coordinates inside font glyphes are clamped between 0.0f and 1.0f */
|
||||
GUI_COORD_PIXEL
|
||||
/* texture coordinates inside font glyphes are in absolute pixel */
|
||||
};
|
||||
|
||||
struct gui_baked_font {
|
||||
gui_float height;
|
||||
/* height of the font */
|
||||
gui_float ascent, descent;
|
||||
/* font glyphes ascent and descent */
|
||||
gui_long glyph_offset;
|
||||
/* glyph array offset inside the font glyph baking output array */
|
||||
gui_long glyph_count;
|
||||
/* number of glyphes of this font inside the glyph baking array output */
|
||||
const gui_long *ranges;
|
||||
/* font codepoint ranges as pairs of (from/to) and 0 as last element */
|
||||
};
|
||||
|
||||
struct gui_font_config {
|
||||
void *ttf_blob;
|
||||
/* pointer to loaded TTF file memory block */
|
||||
gui_size ttf_size;
|
||||
/* size of the loaded TTF file memory block */
|
||||
gui_float size;
|
||||
/* bake pixel height of the font */
|
||||
gui_uint oversample_h, oversample_v;
|
||||
/* rasterize at hight quality for sub-pixel position */
|
||||
gui_bool pixel_snap;
|
||||
/* align very character to pixel boundry (if true set oversample (1,1)) */
|
||||
enum gui_font_coord_type coord_type;
|
||||
/* baked glyph texture coordinate format with either pixel or UV coordinates */
|
||||
struct gui_vec2 spacing;
|
||||
/* extra pixel spacing between glyphs */
|
||||
const gui_long *range;
|
||||
/* list of unicode ranges (2 values per range, zero terminated) */
|
||||
struct gui_baked_font *font;
|
||||
/* font to setup in the baking process */
|
||||
};
|
||||
|
||||
struct gui_font_glyph {
|
||||
gui_long codepoint;
|
||||
/* unicode codepoint */
|
||||
gui_float xadvance;
|
||||
struct gui_vec2 point[2];
|
||||
struct gui_vec2 uv[2];
|
||||
/* xoffset to the next character */
|
||||
gui_float x0, y0, x1, y1;
|
||||
/* glyph bounding points in pixel inside the glyph image with top left and bottom right */
|
||||
gui_float u0, v0, u1, v1;
|
||||
/* texture coordinates either in pixel or clamped (0.0 - 1.0) */
|
||||
};
|
||||
|
||||
struct gui_font {
|
||||
gui_float size;
|
||||
/* pixel height of the font */
|
||||
gui_float scale;
|
||||
struct gui_vec2 offset;
|
||||
/* scale factor for different font size */
|
||||
gui_float ascent, descent;
|
||||
struct gui_buffer glyphes;
|
||||
gui_long fallback_code;
|
||||
struct gui_font_glyph *fallback_glyph;
|
||||
gui_float fallback_xadvance;
|
||||
struct gui_user_font handle;
|
||||
/* font ascent and descent */
|
||||
struct gui_font_glyph *glyphes;
|
||||
/* font glyph array */
|
||||
const struct gui_font_glyph *fallback;
|
||||
/* fallback glyph */
|
||||
gui_long fallback_codepoint;
|
||||
/* fallback glyph codepoint */
|
||||
gui_long glyph_count;
|
||||
/* font glyph array size */
|
||||
const gui_long *ranges;
|
||||
/* glyph unicode ranges in the font */
|
||||
gui_handle atlas;
|
||||
/* font image atlas handle */
|
||||
};
|
||||
|
||||
/* some language glyph codepoint ranges */
|
||||
const gui_long *gui_font_default_glyph_ranges(void);
|
||||
const gui_long *gui_font_chinese_glyph_ranges(void);
|
||||
const gui_long *gui_font_cyrillic_glyph_ranges(void);
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* Baking
|
||||
* ---------------------------------------------------------------*/
|
||||
/* font baking functions (need to be called sequentially top to bottom) */
|
||||
void gui_font_bake_memory(gui_size *temporary_memory, gui_size *glyph_count,
|
||||
struct gui_font_config*, gui_size count);
|
||||
/* this function calculates the needed memory for the baking process
|
||||
Input:
|
||||
- array of configuration for every font that should be baked into one image
|
||||
- number of configuration fonts in the array
|
||||
Output:
|
||||
- amount of memory needed in the baking process
|
||||
- total number of glyphes that need to be allocated
|
||||
*/
|
||||
gui_bool gui_font_bake_pack(gui_size *img_memory, gui_size *img_width, gui_size *img_height,
|
||||
struct gui_recti *custom_space,
|
||||
void *temporary_memory, gui_size temporary_size,
|
||||
const struct gui_font_config*, gui_size font_count);
|
||||
/* this function packs together all glyphes and an optional space into one
|
||||
total image space and returns the needed image width and height.
|
||||
Input:
|
||||
- NULL or custom space inside the image with width and height will be updated!
|
||||
- temporary memory block that will be used in the baking process
|
||||
- size of the temporary memory block
|
||||
- array of configuration for every font that should be baked into one image
|
||||
- number of configuration fonts in the array
|
||||
Output:
|
||||
- calculated resulting size of the image in bytes
|
||||
- pixel width of the resulting image
|
||||
- pixel height of the resulting image
|
||||
- custom space bounds with position and size inside image which can be filled by the user
|
||||
*/
|
||||
void gui_font_bake(void *image_memory, gui_size image_width, gui_size image_height,
|
||||
void *temporary_memory, gui_size temporary_memory_size,
|
||||
struct gui_font_glyph*, gui_size glyphes_count,
|
||||
const struct gui_font_config*, gui_size font_count);
|
||||
/* this function bakes all glyphes into the pre-allocated image and
|
||||
fills a glyph array with information.
|
||||
Input:
|
||||
- image memory buffer to bake the glyph into
|
||||
- pixel width/height of the image
|
||||
- temporary memory block that will be used in the baking process
|
||||
- size of the temporary memory block
|
||||
Output:
|
||||
- image filled with glyphes
|
||||
- filled glyph array
|
||||
*/
|
||||
void gui_font_bake_custom_data(void *img_memory, gui_size img_width, gui_size img_height,
|
||||
struct gui_recti img_dst, const char *texture_data_mask,
|
||||
gui_size tex_width, gui_size tex_height,
|
||||
gui_char white, gui_char black);
|
||||
/* this function bakes custom data in string format with white, black and zero
|
||||
alpha pixels into the font image. The zero alpha pixel is represented as
|
||||
any character beside the black and zero pixel character.
|
||||
Input:
|
||||
- image memory buffer to bake the custom data into
|
||||
- image size (width/height) of the image in pixels
|
||||
- custom texture data in string format
|
||||
- texture size (width/height) of the custom image content
|
||||
- character representing a white pixel in the texture data format
|
||||
- character representing a black pixel in the texture data format
|
||||
Output:
|
||||
- image filled with custom texture data
|
||||
*/
|
||||
void gui_font_bake_convert(void *out_memory, gui_ushort img_width, gui_ushort img_height,
|
||||
const void *in_memory);
|
||||
/* this function converts the alpha8 baking input image into a
|
||||
preallocated rgba8 output image.
|
||||
Input:
|
||||
- image pixel size (width, height)
|
||||
- memory block containing the alpha8 image
|
||||
Output:
|
||||
- rgba8 output image
|
||||
*/
|
||||
/* ---------------------------------------------------------------
|
||||
* Font
|
||||
* ---------------------------------------------------------------*/
|
||||
void gui_font_init(struct gui_font*, gui_float pixel_height,
|
||||
gui_long fallback_codepoint, struct gui_font_glyph*,
|
||||
const struct gui_baked_font*, gui_handle atlas);
|
||||
/* this function initializes a font. IMPORTANT: The font only references
|
||||
* its glyphes since it allows to have multible font glyph in one big array.
|
||||
Input:
|
||||
- pixel height of the font can be different than the baked font height
|
||||
- unicode fallback codepoint for a glyph that will be used if a glyph is requested
|
||||
that does not exist in the font
|
||||
- glyph array of all glyphes inside the font
|
||||
- number of glyphes inside the glyph array
|
||||
- font information for this font from the baking process
|
||||
- handle to the baked font image atlas
|
||||
*/
|
||||
struct gui_user_font gui_font_ref(struct gui_font*);
|
||||
/* this function
|
||||
Output:
|
||||
- gui font handle used in the library
|
||||
*/
|
||||
const struct gui_font_glyph* gui_font_find_glyph(struct gui_font*, gui_long unicode);
|
||||
/* this function
|
||||
Input:
|
||||
- unicode glyph codepoint of the glyph
|
||||
Output:
|
||||
- either the glyph with the given codepoint or a fallback glyph if does not exist
|
||||
*/
|
||||
#endif
|
||||
/*
|
||||
* ===============================================================
|
||||
@ -1377,6 +1596,10 @@ struct gui_font {
|
||||
gui_edit_box_len_char -- returns the length of the string in bytes
|
||||
gui_edit_box_len -- returns the length of the string in glyphes
|
||||
*/
|
||||
typedef gui_bool(*gui_filter)(gui_long unicode);
|
||||
typedef void(*gui_paste_f)(gui_handle, struct gui_edit_box*);
|
||||
typedef void(*gui_copy_f)(gui_handle, const char*, gui_size size);
|
||||
|
||||
struct gui_clipboard {
|
||||
gui_handle userdata;
|
||||
/* user memory for callback */
|
||||
@ -2241,14 +2464,48 @@ void gui_style_reset(struct gui_style*);
|
||||
*
|
||||
* ===============================================================
|
||||
TILING
|
||||
----------------------------
|
||||
Tiling provides a way to divide a space into fixed slots which again can be
|
||||
divided into either horizontal or vertical spaces. The tiled layout consists
|
||||
of five slots (Top, Left, Center, Right and Bottom) what is also known
|
||||
as a Border layout. Since slots can either be filled or empty, horizontally or
|
||||
vertically fillable, have either none, one or many subspaces and can even
|
||||
have a tiled layout inside it is possible to achive a great number of layout.
|
||||
|
||||
In addition it is possible to define the space inside the tiled layout either
|
||||
in pixel or as a ratio. Ratio based layout are great for scaling but
|
||||
are less usefull in cases where scaling destroys the UI. Pixel based layouts
|
||||
provided a layout which always look the same but are less flexible.
|
||||
|
||||
The tiled layout is used in the library inside windows as a widget layout
|
||||
and for window placing inside the screen with each case having its own functions
|
||||
to handle and use the tiled layout.
|
||||
|
||||
USAGE
|
||||
----------------------------
|
||||
To use the tiled layout you have to define which slot inside the layout
|
||||
should be active, how the slot should be filled and how much space the
|
||||
it takes. To define each slot you first have to call `gui_tiled_begin`
|
||||
to start the layout slot definiton and to end the definition
|
||||
the corresponding function `gui_tiled_end` has to be called.
|
||||
Between both sequence points you can define each slot with `gui_tiled_slot`.
|
||||
|
||||
-----------------------------
|
||||
| TOP |
|
||||
-----------------------------
|
||||
| | | |
|
||||
| LEFT | CENTER | RIGHT |
|
||||
| | | |
|
||||
-----------------------------
|
||||
| BOTTOM |
|
||||
-----------------------------
|
||||
|
||||
tiling API
|
||||
----------------------------
|
||||
gui_tiled_begin -- starts the definition of a tiled layout
|
||||
gui_tiled_slot_bounds -- returns the bounds of a slot in the tiled layout
|
||||
gui_tiled_bounds -- returns the bounds of a widget in the tiled layout
|
||||
gui_tiled_slot -- activates and setups a slot inside the tile layout
|
||||
gui_tiled_end -- ends the definiition of the tiled layout slots
|
||||
gui_tiled_slot_bounds -- returns the bounds of a slot in the tiled layout
|
||||
gui_tiled_bounds -- returns the bounds of a widget in the tiled layout
|
||||
*/
|
||||
enum gui_tiled_layout_slot_index {
|
||||
GUI_SLOT_TOP,
|
||||
@ -2301,6 +2558,20 @@ void gui_tiled_begin(struct gui_tiled_layout*, enum gui_layout_format,
|
||||
- pixel width of the tiled layout space
|
||||
- pixel height of the tiled layout space
|
||||
*/
|
||||
void gui_tiled_slot(struct gui_tiled_layout *layout,
|
||||
enum gui_tiled_layout_slot_index, gui_float ratio,
|
||||
enum gui_tiled_slot_format, gui_uint widget_count);
|
||||
/* this functions defines a slot in the tiled layout which then can be filled
|
||||
* with widgets
|
||||
Input:
|
||||
- slot identifier
|
||||
- either ratio or pixel size of the slot
|
||||
- slot filling format with either horizontal or vertical filling
|
||||
- number of widgets inside the slot
|
||||
*/
|
||||
void gui_tiled_end(struct gui_tiled_layout*);
|
||||
/* this functions ends the definitions of the tiled layout slots */
|
||||
|
||||
void gui_tiled_slot_bounds(struct gui_rect*, const struct gui_tiled_layout*,
|
||||
enum gui_tiled_layout_slot_index);
|
||||
/* this functions queries the bounds (position + size) of a tiled layout slot
|
||||
@ -2318,19 +2589,6 @@ void gui_tiled_bounds(struct gui_rect*, const struct gui_tiled_layout*,
|
||||
Output:
|
||||
- rectangle with position and size of the slot entry
|
||||
*/
|
||||
void gui_tiled_slot(struct gui_tiled_layout *layout,
|
||||
enum gui_tiled_layout_slot_index, gui_float ratio,
|
||||
enum gui_tiled_slot_format, gui_uint widget_count);
|
||||
/* this functions defines a slot in the tiled layout which then can be filled
|
||||
* with widgets
|
||||
Input:
|
||||
- slot identifier
|
||||
- either ratio or pixel size of the slot
|
||||
- slot filling format with either horizontal or vertical filling
|
||||
- number of widgets inside the slot
|
||||
*/
|
||||
void gui_tiled_end(struct gui_tiled_layout*);
|
||||
/* this functions ends the definitions of the tiled layout slots */
|
||||
/*
|
||||
* ==============================================================
|
||||
*
|
||||
@ -3322,9 +3580,8 @@ enum gui_popup_type {
|
||||
/* dynamically growing popup with maximum height */
|
||||
};
|
||||
|
||||
gui_flags gui_popup_begin(struct gui_context *parent,
|
||||
struct gui_context *popup, enum gui_popup_type,
|
||||
gui_flags, struct gui_rect bounds,
|
||||
gui_flags gui_popup_begin(struct gui_context *parent, struct gui_context *popup,
|
||||
enum gui_popup_type, gui_flags, struct gui_rect bounds,
|
||||
struct gui_vec2 offset);
|
||||
/* this function adds a overlapping blocking popup menu
|
||||
Input:
|
||||
@ -3606,9 +3863,8 @@ struct gui_tree {
|
||||
/* current depth of the tree */
|
||||
};
|
||||
|
||||
void gui_tree_begin(struct gui_context*, struct gui_tree*,
|
||||
const char*, gui_float row_height,
|
||||
struct gui_vec2 scrollbar);
|
||||
void gui_tree_begin(struct gui_context*, struct gui_tree*, const char *title,
|
||||
gui_float row_height, struct gui_vec2 scrollbar);
|
||||
/* this function begins the tree building process
|
||||
Input:
|
||||
- title describing the tree or NULL
|
||||
|
545
stb_rect_pack.h
Normal file
545
stb_rect_pack.h
Normal file
@ -0,0 +1,545 @@
|
||||
/* stb_rect_pack.h - v0.05 - public domain - rectangle packing */
|
||||
/* Sean Barrett 2014 */
|
||||
/* */
|
||||
/* Useful for e.g. packing rectangular textures into an atlas. */
|
||||
/* Does not do rotation. */
|
||||
/* */
|
||||
/* Not necessarily the awesomest packing method, but better than */
|
||||
/* the totally naive one in stb_truetype (which is primarily what */
|
||||
/* this is meant to replace). */
|
||||
/* */
|
||||
/* Has only had a few tests run, may have issues. */
|
||||
/* */
|
||||
/* More docs to come. */
|
||||
/* */
|
||||
/* No memory allocations; uses qsort() and assert() from stdlib. */
|
||||
/* */
|
||||
/* This library currently uses the Skyline Bottom-Left algorithm. */
|
||||
/* */
|
||||
/* Please note: better rectangle packers are welcome! Please */
|
||||
/* implement them to the same API, but with a different init */
|
||||
/* function. */
|
||||
/* */
|
||||
/* Version history: */
|
||||
/* */
|
||||
/* 0.05: added STBRP_ASSERT to allow replacing assert */
|
||||
/* 0.04: fixed minor bug in STBRP_LARGE_RECTS support */
|
||||
/* 0.01: initial release */
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////// */
|
||||
/* */
|
||||
/* INCLUDE SECTION */
|
||||
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||
|
||||
#define STB_RECT_PACK_VERSION 1
|
||||
|
||||
#ifdef STBRP_STATIC
|
||||
#define STBRP_DEF static
|
||||
#else
|
||||
#define STBRP_DEF extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct stbrp_context stbrp_context;
|
||||
typedef struct stbrp_node stbrp_node;
|
||||
typedef struct stbrp_rect stbrp_rect;
|
||||
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
typedef int stbrp_coord;
|
||||
#else
|
||||
typedef unsigned short stbrp_coord;
|
||||
#endif
|
||||
|
||||
STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||
/* Assign packed locations to rectangles. The rectangles are of type */
|
||||
/* 'stbrp_rect' defined below, stored in the array 'rects', and there */
|
||||
/* are 'num_rects' many of them. */
|
||||
/* */
|
||||
/* Rectangles which are successfully packed have the 'was_packed' flag */
|
||||
/* set to a non-zero value and 'x' and 'y' store the minimum location */
|
||||
/* on each axis (i.e. bottom-left in cartesian coordinates, top-left */
|
||||
/* if you imagine y increasing downwards). Rectangles which do not fit */
|
||||
/* have the 'was_packed' flag set to 0. */
|
||||
/* */
|
||||
/* You should not try to access the 'rects' array from another thread */
|
||||
/* while this function is running, as the function temporarily reorders */
|
||||
/* the array while it executes. */
|
||||
/* */
|
||||
/* To pack into another rectangle, you need to call stbrp_init_target */
|
||||
/* again. To continue packing into the same rectangle, you can call */
|
||||
/* this function again. Calling this multiple times with multiple rect */
|
||||
/* arrays will probably produce worse packing results than calling it */
|
||||
/* a single time with the full rectangle array, but the option is */
|
||||
/* available. */
|
||||
|
||||
struct stbrp_rect
|
||||
{
|
||||
/* reserved for your use: */
|
||||
int id;
|
||||
|
||||
/* input: */
|
||||
stbrp_coord w, h;
|
||||
|
||||
/* output: */
|
||||
stbrp_coord x, y;
|
||||
int was_packed; /* non-zero if valid packing */
|
||||
|
||||
}; /* 16 bytes, nominally */
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||
/* Initialize a rectangle packer to: */
|
||||
/* pack a rectangle that is 'width' by 'height' in dimensions */
|
||||
/* using temporary storage provided by the array 'nodes', which is 'num_nodes' long */
|
||||
/* */
|
||||
/* You must call this function every time you start packing into a new target. */
|
||||
/* */
|
||||
/* There is no "shutdown" function. The 'nodes' memory must stay valid for */
|
||||
/* the following stbrp_pack_rects() call (or calls), but can be freed after */
|
||||
/* the call (or calls) finish. */
|
||||
/* */
|
||||
/* Note: to guarantee best results, either: */
|
||||
/* 1. make sure 'num_nodes' >= 'width' */
|
||||
/* or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' */
|
||||
/* */
|
||||
/* If you don't do either of the above things, widths will be quantized to multiples */
|
||||
/* of small integers to guarantee the algorithm doesn't run out of temporary storage. */
|
||||
/* */
|
||||
/* If you do #2, then the non-quantized algorithm will be used, but the algorithm */
|
||||
/* may run out of temporary storage and be unable to pack some rectangles. */
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||
/* Optionally call this function after init but before doing any packing to */
|
||||
/* change the handling of the out-of-temp-memory scenario, described above. */
|
||||
/* If you call init again, this will be reset to the default (false). */
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||
/* Optionally select which packing heuristic the library should use. Different */
|
||||
/* heuristics will produce better/worse results for different data sets. */
|
||||
/* If you call init again, this will be reset to the default. */
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP_HEURISTIC_Skyline_default=0,
|
||||
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||
};
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////// */
|
||||
/* */
|
||||
/* the details of the following structures don't matter to you, but they must */
|
||||
/* be visible so you can handle the memory allocations for them */
|
||||
|
||||
struct stbrp_node
|
||||
{
|
||||
stbrp_coord x,y;
|
||||
stbrp_node *next;
|
||||
};
|
||||
|
||||
struct stbrp_context
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int align;
|
||||
int init_mode;
|
||||
int heuristic;
|
||||
int num_nodes;
|
||||
stbrp_node *active_head;
|
||||
stbrp_node *free_head;
|
||||
stbrp_node extra[2]; /* we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' */
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////// */
|
||||
/* */
|
||||
/* IMPLEMENTATION SECTION */
|
||||
/* */
|
||||
|
||||
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef STBRP_ASSERT
|
||||
#include <assert.h>
|
||||
#define STBRP_ASSERT assert
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP__INIT_skyline = 1
|
||||
};
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||
{
|
||||
switch (context->init_mode) {
|
||||
case STBRP__INIT_skyline:
|
||||
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||
context->heuristic = heuristic;
|
||||
break;
|
||||
default:
|
||||
STBRP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||
{
|
||||
if (allow_out_of_mem)
|
||||
/* if it's ok to run out of memory, then don't bother aligning them; */
|
||||
/* this gives better packing, but may fail due to OOM (even though */
|
||||
/* the rectangles easily fit). @TODO a smarter approach would be to only */
|
||||
/* quantize once we've hit OOM, then we could get rid of this parameter. */
|
||||
context->align = 1;
|
||||
else {
|
||||
/* if it's not ok to run out of memory, then quantize the widths */
|
||||
/* so that num_nodes is always enough nodes. */
|
||||
/* */
|
||||
/* I.e. num_nodes * align >= width */
|
||||
/* align >= width / num_nodes */
|
||||
/* align = ceil(width/num_nodes) */
|
||||
|
||||
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||
{
|
||||
int i;
|
||||
#ifndef STBRP_LARGE_RECTS
|
||||
STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
|
||||
#endif
|
||||
|
||||
for (i=0; i < num_nodes-1; ++i)
|
||||
nodes[i].next = &nodes[i+1];
|
||||
nodes[i].next = NULL;
|
||||
context->init_mode = STBRP__INIT_skyline;
|
||||
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||
context->free_head = &nodes[0];
|
||||
context->active_head = &context->extra[0];
|
||||
context->width = width;
|
||||
context->height = height;
|
||||
context->num_nodes = num_nodes;
|
||||
stbrp_setup_allow_out_of_mem(context, 0);
|
||||
|
||||
/* node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) */
|
||||
context->extra[0].x = 0;
|
||||
context->extra[0].y = 0;
|
||||
context->extra[0].next = &context->extra[1];
|
||||
context->extra[1].x = (stbrp_coord) width;
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
context->extra[1].y = (1<<30);
|
||||
#else
|
||||
context->extra[1].y = 65535;
|
||||
#endif
|
||||
context->extra[1].next = NULL;
|
||||
}
|
||||
|
||||
/* find minimum y position if it starts at x1 */
|
||||
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||
{
|
||||
stbrp_node *node = first;
|
||||
int x1 = x0 + width;
|
||||
int min_y, visited_width, waste_area;
|
||||
STBRP_ASSERT(first->x <= x0);
|
||||
(void)c;
|
||||
|
||||
#if 0
|
||||
/* skip in case we're past the node */
|
||||
while (node->next->x <= x0)
|
||||
++node;
|
||||
#else
|
||||
STBRP_ASSERT(node->next->x > x0); /* we ended up handling this in the caller for efficiency */
|
||||
#endif
|
||||
|
||||
STBRP_ASSERT(node->x <= x0);
|
||||
|
||||
min_y = 0;
|
||||
waste_area = 0;
|
||||
visited_width = 0;
|
||||
while (node->x < x1) {
|
||||
if (node->y > min_y) {
|
||||
/* raise min_y higher. */
|
||||
/* we've accounted for all waste up to min_y, */
|
||||
/* but we'll now add more waste for everything we've visted */
|
||||
waste_area += visited_width * (node->y - min_y);
|
||||
min_y = node->y;
|
||||
/* the first time through, visited_width might be reduced */
|
||||
if (node->x < x0)
|
||||
visited_width += node->next->x - x0;
|
||||
else
|
||||
visited_width += node->next->x - node->x;
|
||||
} else {
|
||||
/* add waste area */
|
||||
int under_width = node->next->x - node->x;
|
||||
if (under_width + visited_width > width)
|
||||
under_width = width - visited_width;
|
||||
waste_area += under_width * (min_y - node->y);
|
||||
visited_width += under_width;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
*pwaste = waste_area;
|
||||
return min_y;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x,y;
|
||||
stbrp_node **prev_link;
|
||||
} stbrp__findresult;
|
||||
|
||||
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||
{
|
||||
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||
stbrp__findresult fr;
|
||||
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||
|
||||
/* align to multiple of c->align */
|
||||
width = (width + c->align - 1);
|
||||
width -= width % c->align;
|
||||
STBRP_ASSERT(width % c->align == 0);
|
||||
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
while (node->x + width <= c->width) {
|
||||
int y,waste;
|
||||
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { /* actually just want to test BL */
|
||||
/* bottom left */
|
||||
if (y < best_y) {
|
||||
best_y = y;
|
||||
best = prev;
|
||||
}
|
||||
} else {
|
||||
/* best-fit */
|
||||
if (y + height <= c->height) {
|
||||
/* can only use it if it first vertically */
|
||||
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||
|
||||
/* if doing best-fit (BF), we also have to try aligning right edge to each node position */
|
||||
/* */
|
||||
/* e.g, if fitting */
|
||||
/* */
|
||||
/* ____________________ */
|
||||
/* |____________________| */
|
||||
/* */
|
||||
/* into */
|
||||
/* */
|
||||
/* | | */
|
||||
/* | ____________| */
|
||||
/* |____________| */
|
||||
/* */
|
||||
/* then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned */
|
||||
/* */
|
||||
/* This makes BF take about 2x the time */
|
||||
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||
tail = c->active_head;
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
/* find first node that's admissible */
|
||||
while (tail->x < width)
|
||||
tail = tail->next;
|
||||
while (tail) {
|
||||
int xpos = tail->x - width;
|
||||
int y,waste;
|
||||
STBRP_ASSERT(xpos >= 0);
|
||||
/* find the left position that matches this */
|
||||
while (node->next->x <= xpos) {
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||
if (y + height < c->height) {
|
||||
if (y <= best_y) {
|
||||
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||
best_x = xpos;
|
||||
STBRP_ASSERT(y <= best_y);
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
fr.prev_link = best;
|
||||
fr.x = best_x;
|
||||
fr.y = best_y;
|
||||
return fr;
|
||||
}
|
||||
|
||||
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||
{
|
||||
/* find best position according to heuristic */
|
||||
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||
stbrp_node *node, *cur;
|
||||
|
||||
/* bail if: */
|
||||
/* 1. it failed */
|
||||
/* 2. the best node doesn't fit (we don't always check this) */
|
||||
/* 3. we're out of memory */
|
||||
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||
res.prev_link = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* on success, create new node */
|
||||
node = context->free_head;
|
||||
node->x = (stbrp_coord) res.x;
|
||||
node->y = (stbrp_coord) (res.y + height);
|
||||
|
||||
context->free_head = node->next;
|
||||
|
||||
/* insert the new node into the right starting point, and */
|
||||
/* let 'cur' point to the remaining nodes needing to be */
|
||||
/* stiched back in */
|
||||
|
||||
cur = *res.prev_link;
|
||||
if (cur->x < res.x) {
|
||||
/* preserve the existing one, so start testing with the next one */
|
||||
stbrp_node *next = cur->next;
|
||||
cur->next = node;
|
||||
cur = next;
|
||||
} else {
|
||||
*res.prev_link = node;
|
||||
}
|
||||
|
||||
/* from here, traverse cur and free the nodes, until we get to one */
|
||||
/* that shouldn't be freed */
|
||||
while (cur->next && cur->next->x <= res.x + width) {
|
||||
stbrp_node *next = cur->next;
|
||||
/* move the current node to the free list */
|
||||
cur->next = context->free_head;
|
||||
context->free_head = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
/* stitch the list back in */
|
||||
node->next = cur;
|
||||
|
||||
if (cur->x < res.x + width)
|
||||
cur->x = (stbrp_coord) (res.x + width);
|
||||
|
||||
#ifdef _DEBUG
|
||||
cur = context->active_head;
|
||||
while (cur->x < context->width) {
|
||||
STBRP_ASSERT(cur->x < cur->next->x);
|
||||
cur = cur->next;
|
||||
}
|
||||
STBRP_ASSERT(cur->next == NULL);
|
||||
|
||||
{
|
||||
stbrp_node *L1 = NULL, *L2 = NULL;
|
||||
int count=0;
|
||||
cur = context->active_head;
|
||||
while (cur) {
|
||||
L1 = cur;
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
cur = context->free_head;
|
||||
while (cur) {
|
||||
L2 = cur;
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
STBRP_ASSERT(count == context->num_nodes+2);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int rect_height_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
if (p->h > q->h)
|
||||
return -1;
|
||||
if (p->h < q->h)
|
||||
return 1;
|
||||
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||
}
|
||||
|
||||
static int rect_width_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
if (p->w > q->w)
|
||||
return -1;
|
||||
if (p->w < q->w)
|
||||
return 1;
|
||||
return (p->h > q->h) ? -1 : (p->h < q->h);
|
||||
}
|
||||
|
||||
static int rect_original_order(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||
}
|
||||
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
#define STBRP__MAXVAL 0xffffffff
|
||||
#else
|
||||
#define STBRP__MAXVAL 0xffff
|
||||
#endif
|
||||
|
||||
STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* we use the 'was_packed' field internally to allow sorting/unsorting */
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = i;
|
||||
#ifndef STBRP_LARGE_RECTS
|
||||
STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* sort according to heuristic */
|
||||
qsort(rects, (size_t)num_rects, sizeof(rects[0]), rect_height_compare);
|
||||
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||
if (fr.prev_link) {
|
||||
rects[i].x = (stbrp_coord) fr.x;
|
||||
rects[i].y = (stbrp_coord) fr.y;
|
||||
} else {
|
||||
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* unsort */
|
||||
qsort(rects, (size_t)num_rects, sizeof(rects[0]), rect_original_order);
|
||||
|
||||
/* set was_packed flags */
|
||||
for (i=0; i < num_rects; ++i)
|
||||
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||
}
|
||||
#endif
|
3218
stb_truetype.h
Normal file
3218
stb_truetype.h
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user