├── .gitignore ├── CMakeLists.txt ├── README.md ├── config.h.in ├── doc └── screenshot │ ├── 01.png │ └── nano.gif ├── fontstash.h ├── main.cpp ├── nanoui.cpp ├── nanoui.h ├── nanoui_matrix.h ├── nanovg.c ├── nanovg.h ├── nanovg_gl.h ├── nanovg_gl_utils.h ├── platforms ├── linux │ └── include │ │ └── GL │ │ └── glcorearb.h └── windows │ ├── include │ ├── GL │ │ ├── glcorearb.h │ │ ├── glew.h │ │ ├── glxew.h │ │ └── wglew.h │ └── GLFW │ │ ├── glfw3.h │ │ └── glfw3native.h │ └── lib │ ├── glew32s.lib │ └── glfw3.lib ├── res ├── Roboto-Bold.ttf ├── Roboto-Light.ttf ├── Roboto-Regular.ttf └── entypo.ttf ├── stb_image.h └── stb_truetype.h /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.8) 2 | 3 | #--------------------------------------------------------------- 4 | # Project 5 | #--------------------------------------------------------------- 6 | 7 | PROJECT(nanoui) 8 | set(LIBRARY_NAME nanoui) 9 | 10 | SET(version_major 0) 11 | SET(version_minor 1) 12 | SET(version_patch 1) 13 | 14 | configure_file ( 15 | "${PROJECT_SOURCE_DIR}/config.h.in" 16 | "${PROJECT_BINARY_DIR}/config.h" 17 | ) 18 | 19 | #--------------------------------------------------------------- 20 | # Platform 21 | #--------------------------------------------------------------- 22 | if (MSVC) 23 | 24 | set( PLATFORM_INCLUDE_DIR 25 | ${PROJECT_SOURCE_DIR}/platforms/windows/include 26 | ) 27 | 28 | add_definitions(-DNANOVG_GLEW ) 29 | add_definitions(-DNANOVG_GL3_IMPLEMENTATION ) 30 | 31 | elseif (ANDROID) 32 | 33 | elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") 34 | 35 | set( PLATFORM_INCLUDE_DIR 36 | ${PROJECT_SOURCE_DIR}/platforms/linux/include 37 | ) 38 | 39 | set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -fPIC" ) 40 | set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -g -fPIC" ) 41 | 42 | add_definitions(-DNANOVG_GL3_IMPLEMENTATION ) 43 | 44 | endif () 45 | 46 | #--------------------------------------------------------------- 47 | # Defines 48 | #--------------------------------------------------------------- 49 | 50 | # include 51 | set(INCLUDE_DIR 52 | ${PROJECT_SOURCE_DIR} 53 | ${PLATFORM_INCLUDE_DIR} 54 | ) 55 | 56 | set (SOURCES 57 | main.cpp 58 | nanoui.cpp 59 | nanovg.c 60 | ) 61 | 62 | include_directories(${INCLUDE_DIR}) 63 | 64 | add_executable( 65 | ${LIBRARY_NAME} 66 | ${SOURCES} 67 | ) 68 | 69 | 70 | #--------------------------------------------------------------- 71 | # Sub Modules 72 | #--------------------------------------------------------------- 73 | 74 | 75 | #--------------------------------------------------------------- 76 | # Link 77 | #--------------------------------------------------------------- 78 | if (MSVC) 79 | 80 | set (EXTERNAL_LIBS 81 | ${PROJECT_SOURCE_DIR}/platforms/windows/lib/glfw3.lib 82 | ${PROJECT_SOURCE_DIR}/platforms/windows/lib/glew32s.lib 83 | opengl32 84 | ) 85 | 86 | elseif (ANDROID) 87 | 88 | 89 | elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") 90 | 91 | set (EXTERNAL_LIBS 92 | GL 93 | GLU 94 | m 95 | GLEW 96 | glfw3 97 | Xxf86vm 98 | pthread 99 | X11 100 | Xrandr 101 | Xinerama 102 | Xi 103 | Xcursor 104 | rt 105 | ) 106 | 107 | endif (MSVC) 108 | 109 | target_link_libraries( 110 | ${LIBRARY_NAME} 111 | ${EXTERNAL_LIBS} 112 | ) 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nanoui 2 | 3 | nanoui is a vector graphical user interface library written in C++, based on [NanoVG](https://github.com/memononen/nanovg). 4 | 5 | ![screenshot of some text rendered witht the sample program](/doc/screenshot/01.png?raw=true) 6 | ![screenshot of some text rendered witht the sample program](/doc/screenshot/nano.gif?raw=true) 7 | 8 | ## Project Goals 9 | 10 | The goal of the nanoui is to provide the simplest and fastest vector graphical user interface library. 11 | 12 | ## Status 13 | 14 | on very early stage. 15 | 16 | ## Usage 17 | 18 | prepering. 19 | 20 | ## License 21 | 22 | Distributed under the [MIT License](LICENSE). 23 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | #define VERSION_MAJOR @version_major@ 2 | #define VERSION_MINOR @version_minor@ 3 | #define VERSION_PATCH @version_patch@ 4 | #define WIN_GL_LIBRARY "@WIN_GL_LIBRARY@" 5 | -------------------------------------------------------------------------------- /doc/screenshot/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/miyax/nanoui/7e1616f865b77ba94cebe8fd6ce2b609a040c517/doc/screenshot/01.png -------------------------------------------------------------------------------- /doc/screenshot/nano.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/miyax/nanoui/7e1616f865b77ba94cebe8fd6ce2b609a040c517/doc/screenshot/nano.gif -------------------------------------------------------------------------------- /fontstash.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2009-2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | 19 | #ifndef FONS_H 20 | #define FONS_H 21 | 22 | #define FONS_INVALID -1 23 | 24 | enum FONSflags { 25 | FONS_ZERO_TOPLEFT = 1, 26 | FONS_ZERO_BOTTOMLEFT = 2, 27 | }; 28 | 29 | enum FONSalign { 30 | // Horizontal align 31 | FONS_ALIGN_LEFT = 1<<0, // Default 32 | FONS_ALIGN_CENTER = 1<<1, 33 | FONS_ALIGN_RIGHT = 1<<2, 34 | // Vertical align 35 | FONS_ALIGN_TOP = 1<<3, 36 | FONS_ALIGN_MIDDLE = 1<<4, 37 | FONS_ALIGN_BOTTOM = 1<<5, 38 | FONS_ALIGN_BASELINE = 1<<6, // Default 39 | }; 40 | 41 | enum FONSerrorCode { 42 | // Font atlas is full. 43 | FONS_ATLAS_FULL = 1, 44 | // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE. 45 | FONS_SCRATCH_FULL = 2, 46 | // Calls to fonsPushState has craeted too large stack, if you need deep state stack bump up FONS_MAX_STATES. 47 | FONS_STATES_OVERFLOW = 3, 48 | // Trying to pop too many states fonsPopState(). 49 | FONS_STATES_UNDERFLOW = 4, 50 | }; 51 | 52 | struct FONSparams { 53 | int width, height; 54 | unsigned char flags; 55 | void* userPtr; 56 | int (*renderCreate)(void* uptr, int width, int height); 57 | int (*renderResize)(void* uptr, int width, int height); 58 | void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data); 59 | void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts); 60 | void (*renderDelete)(void* uptr); 61 | }; 62 | typedef struct FONSparams FONSparams; 63 | 64 | struct FONSquad 65 | { 66 | float x0,y0,s0,t0; 67 | float x1,y1,s1,t1; 68 | }; 69 | typedef struct FONSquad FONSquad; 70 | 71 | struct FONStextIter { 72 | float x, y, nextx, nexty, scale, spacing; 73 | unsigned int codepoint; 74 | short isize, iblur; 75 | struct FONSfont* font; 76 | int prevGlyphIndex; 77 | const char* str; 78 | const char* next; 79 | const char* end; 80 | unsigned int utf8state; 81 | }; 82 | typedef struct FONStextIter FONStextIter; 83 | 84 | typedef struct FONScontext FONScontext; 85 | 86 | // Contructor and destructor. 87 | FONScontext* fonsCreateInternal(FONSparams* params); 88 | void fonsDeleteInternal(FONScontext* s); 89 | 90 | void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr); 91 | // Returns current atlas size. 92 | void fonsGetAtlasSize(FONScontext* s, int* width, int* height); 93 | // Expands the atlas size. 94 | int fonsExpandAtlas(FONScontext* s, int width, int height); 95 | // Reseta the whole stash. 96 | int fonsResetAtlas(FONScontext* stash, int width, int height); 97 | 98 | // Add fonts 99 | int fonsAddFont(FONScontext* s, const char* name, const char* path); 100 | int fonsAddFontMem(FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData); 101 | int fonsGetFontByName(FONScontext* s, const char* name); 102 | 103 | // State handling 104 | void fonsPushState(FONScontext* s); 105 | void fonsPopState(FONScontext* s); 106 | void fonsClearState(FONScontext* s); 107 | 108 | // State setting 109 | void fonsSetSize(FONScontext* s, float size); 110 | void fonsSetColor(FONScontext* s, unsigned int color); 111 | void fonsSetSpacing(FONScontext* s, float spacing); 112 | void fonsSetBlur(FONScontext* s, float blur); 113 | void fonsSetAlign(FONScontext* s, int align); 114 | void fonsSetFont(FONScontext* s, int font); 115 | 116 | // Draw text 117 | float fonsDrawText(FONScontext* s, float x, float y, const char* string, const char* end); 118 | 119 | // Measure text 120 | float fonsTextBounds(FONScontext* s, float x, float y, const char* string, const char* end, float* bounds); 121 | void fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy); 122 | void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh); 123 | 124 | // Text iterator 125 | int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end); 126 | int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad); 127 | 128 | // Pull texture changes 129 | const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height); 130 | int fonsValidateTexture(FONScontext* s, int* dirty); 131 | 132 | // Draws the stash texture for debugging 133 | void fonsDrawDebug(FONScontext* s, float x, float y); 134 | 135 | #endif // FONTSTASH_H 136 | 137 | 138 | #ifdef FONTSTASH_IMPLEMENTATION 139 | 140 | #define FONS_NOTUSED(v) (void)sizeof(v) 141 | 142 | #ifdef FONS_USE_FREETYPE 143 | 144 | #include 145 | #include FT_FREETYPE_H 146 | #include FT_ADVANCES_H 147 | #include 148 | 149 | struct FONSttFontImpl { 150 | FT_Face font; 151 | }; 152 | typedef struct FONSttFontImpl FONSttFontImpl; 153 | 154 | static FT_Library ftLibrary; 155 | 156 | int fons__tt_init() 157 | { 158 | FT_Error ftError; 159 | ftError = FT_Init_FreeType(&ftLibrary); 160 | return ftError == 0; 161 | } 162 | 163 | int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize) 164 | { 165 | FT_Error ftError; 166 | FONS_NOTUSED(context); 167 | 168 | //font->font.userdata = stash; 169 | ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font); 170 | return ftError == 0; 171 | } 172 | 173 | void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) 174 | { 175 | *ascent = font->font->ascender; 176 | *descent = font->font->descender; 177 | *lineGap = font->font->height - (*ascent - *descent); 178 | } 179 | 180 | float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) 181 | { 182 | return size / (font->font->ascender - font->font->descender); 183 | } 184 | 185 | int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) 186 | { 187 | return FT_Get_Char_Index(font->font, codepoint); 188 | } 189 | 190 | int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, 191 | int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) 192 | { 193 | FT_Error ftError; 194 | FT_GlyphSlot ftGlyph; 195 | FONS_NOTUSED(scale); 196 | 197 | ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); 198 | if (ftError) return 0; 199 | ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER); 200 | if (ftError) return 0; 201 | ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, (FT_Fixed*)advance); 202 | if (ftError) return 0; 203 | ftGlyph = font->font->glyph; 204 | *lsb = ftGlyph->metrics.horiBearingX; 205 | *x0 = ftGlyph->bitmap_left; 206 | *x1 = *x0 + ftGlyph->bitmap.width; 207 | *y0 = -ftGlyph->bitmap_top; 208 | *y1 = *y0 + ftGlyph->bitmap.rows; 209 | return 1; 210 | } 211 | 212 | void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, 213 | float scaleX, float scaleY, int glyph) 214 | { 215 | FT_GlyphSlot ftGlyph = font->font->glyph; 216 | int ftGlyphOffset = 0; 217 | int x, y; 218 | FONS_NOTUSED(outWidth); 219 | FONS_NOTUSED(outHeight); 220 | FONS_NOTUSED(scaleX); 221 | FONS_NOTUSED(scaleY); 222 | FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap 223 | 224 | for ( y = 0; y < ftGlyph->bitmap.rows; y++ ) { 225 | for ( x = 0; x < ftGlyph->bitmap.width; x++ ) { 226 | output[(y * outStride) + x] = ftGlyph->bitmap.buffer[ftGlyphOffset++]; 227 | } 228 | } 229 | } 230 | 231 | int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) 232 | { 233 | FT_Vector ftKerning; 234 | FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); 235 | return ftKerning.x; 236 | } 237 | 238 | #else 239 | 240 | #define STB_TRUETYPE_IMPLEMENTATION 241 | static void* fons__tmpalloc(size_t size, void* up); 242 | static void fons__tmpfree(void* ptr, void* up); 243 | #define STBTT_malloc(x,u) fons__tmpalloc(x,u) 244 | #define STBTT_free(x,u) fons__tmpfree(x,u) 245 | #include "stb_truetype.h" 246 | 247 | struct FONSttFontImpl { 248 | stbtt_fontinfo font; 249 | }; 250 | typedef struct FONSttFontImpl FONSttFontImpl; 251 | 252 | int fons__tt_init(FONScontext *context) 253 | { 254 | FONS_NOTUSED(context); 255 | return 1; 256 | } 257 | 258 | int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize) 259 | { 260 | int stbError; 261 | FONS_NOTUSED(dataSize); 262 | 263 | font->font.userdata = context; 264 | stbError = stbtt_InitFont(&font->font, data, 0); 265 | return stbError; 266 | } 267 | 268 | void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) 269 | { 270 | stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap); 271 | } 272 | 273 | float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) 274 | { 275 | return stbtt_ScaleForPixelHeight(&font->font, size); 276 | } 277 | 278 | int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) 279 | { 280 | return stbtt_FindGlyphIndex(&font->font, codepoint); 281 | } 282 | 283 | int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, 284 | int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) 285 | { 286 | FONS_NOTUSED(size); 287 | stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb); 288 | stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1); 289 | return 1; 290 | } 291 | 292 | void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, 293 | float scaleX, float scaleY, int glyph) 294 | { 295 | stbtt_MakeGlyphBitmap(&font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph); 296 | } 297 | 298 | int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) 299 | { 300 | return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2); 301 | } 302 | 303 | #endif 304 | 305 | #ifndef FONS_SCRATCH_BUF_SIZE 306 | # define FONS_SCRATCH_BUF_SIZE 16000 307 | #endif 308 | #ifndef FONS_HASH_LUT_SIZE 309 | # define FONS_HASH_LUT_SIZE 256 310 | #endif 311 | #ifndef FONS_INIT_FONTS 312 | # define FONS_INIT_FONTS 4 313 | #endif 314 | #ifndef FONS_INIT_GLYPHS 315 | # define FONS_INIT_GLYPHS 256 316 | #endif 317 | #ifndef FONS_INIT_ATLAS_NODES 318 | # define FONS_INIT_ATLAS_NODES 256 319 | #endif 320 | #ifndef FONS_VERTEX_COUNT 321 | # define FONS_VERTEX_COUNT 1024 322 | #endif 323 | #ifndef FONS_MAX_STATES 324 | # define FONS_MAX_STATES 20 325 | #endif 326 | 327 | static unsigned int fons__hashint(unsigned int a) 328 | { 329 | a += ~(a<<15); 330 | a ^= (a>>10); 331 | a += (a<<3); 332 | a ^= (a>>6); 333 | a += ~(a<<11); 334 | a ^= (a>>16); 335 | return a; 336 | } 337 | 338 | static int fons__mini(int a, int b) 339 | { 340 | return a < b ? a : b; 341 | } 342 | 343 | static int fons__maxi(int a, int b) 344 | { 345 | return a > b ? a : b; 346 | } 347 | 348 | struct FONSglyph 349 | { 350 | unsigned int codepoint; 351 | int index; 352 | int next; 353 | short size, blur; 354 | short x0,y0,x1,y1; 355 | short xadv,xoff,yoff; 356 | }; 357 | typedef struct FONSglyph FONSglyph; 358 | 359 | struct FONSfont 360 | { 361 | FONSttFontImpl font; 362 | char name[64]; 363 | unsigned char* data; 364 | int dataSize; 365 | unsigned char freeData; 366 | float ascender; 367 | float descender; 368 | float lineh; 369 | FONSglyph* glyphs; 370 | int cglyphs; 371 | int nglyphs; 372 | int lut[FONS_HASH_LUT_SIZE]; 373 | }; 374 | typedef struct FONSfont FONSfont; 375 | 376 | struct FONSstate 377 | { 378 | int font; 379 | int align; 380 | float size; 381 | unsigned int color; 382 | float blur; 383 | float spacing; 384 | }; 385 | typedef struct FONSstate FONSstate; 386 | 387 | struct FONSatlasNode { 388 | short x, y, width; 389 | }; 390 | typedef struct FONSatlasNode FONSatlasNode; 391 | 392 | struct FONSatlas 393 | { 394 | int width, height; 395 | FONSatlasNode* nodes; 396 | int nnodes; 397 | int cnodes; 398 | }; 399 | typedef struct FONSatlas FONSatlas; 400 | 401 | struct FONScontext 402 | { 403 | FONSparams params; 404 | float itw,ith; 405 | unsigned char* texData; 406 | int dirtyRect[4]; 407 | FONSfont** fonts; 408 | FONSatlas* atlas; 409 | int cfonts; 410 | int nfonts; 411 | float verts[FONS_VERTEX_COUNT*2]; 412 | float tcoords[FONS_VERTEX_COUNT*2]; 413 | unsigned int colors[FONS_VERTEX_COUNT]; 414 | int nverts; 415 | unsigned char* scratch; 416 | int nscratch; 417 | FONSstate states[FONS_MAX_STATES]; 418 | int nstates; 419 | void (*handleError)(void* uptr, int error, int val); 420 | void* errorUptr; 421 | }; 422 | 423 | static void* fons__tmpalloc(size_t size, void* up) 424 | { 425 | unsigned char* ptr; 426 | FONScontext* stash = (FONScontext*)up; 427 | 428 | // 16-byte align the returned pointer 429 | size = (size + 0xf) & ~0xf; 430 | 431 | if (stash->nscratch+(int)size > FONS_SCRATCH_BUF_SIZE) { 432 | if (stash->handleError) 433 | stash->handleError(stash->errorUptr, FONS_SCRATCH_FULL, stash->nscratch+(int)size); 434 | return NULL; 435 | } 436 | ptr = stash->scratch + stash->nscratch; 437 | stash->nscratch += (int)size; 438 | return ptr; 439 | } 440 | 441 | static void fons__tmpfree(void* ptr, void* up) 442 | { 443 | (void)ptr; 444 | (void)up; 445 | // empty 446 | } 447 | 448 | // Copyright (c) 2008-2010 Bjoern Hoehrmann 449 | // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. 450 | 451 | #define FONS_UTF8_ACCEPT 0 452 | #define FONS_UTF8_REJECT 12 453 | 454 | static unsigned int fons__decutf8(unsigned int* state, unsigned int* codep, unsigned int byte) 455 | { 456 | static const unsigned char utf8d[] = { 457 | // The first part of the table maps bytes to character classes that 458 | // to reduce the size of the transition table and create bitmasks. 459 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 460 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 461 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 462 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 463 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 464 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 465 | 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 466 | 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, 467 | 468 | // The second part is a transition table that maps a combination 469 | // of a state of the automaton and a character class to a state. 470 | 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, 471 | 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, 472 | 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, 473 | 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, 474 | 12,36,12,12,12,12,12,12,12,12,12,12, 475 | }; 476 | 477 | unsigned int type = utf8d[byte]; 478 | 479 | *codep = (*state != FONS_UTF8_ACCEPT) ? 480 | (byte & 0x3fu) | (*codep << 6) : 481 | (0xff >> type) & (byte); 482 | 483 | *state = utf8d[256 + *state + type]; 484 | return *state; 485 | } 486 | 487 | // Atlas based on Skyline Bin Packer by Jukka Jylänki 488 | 489 | static void fons__deleteAtlas(FONSatlas* atlas) 490 | { 491 | if (atlas == NULL) return; 492 | if (atlas->nodes != NULL) free(atlas->nodes); 493 | free(atlas); 494 | } 495 | 496 | static FONSatlas* fons__allocAtlas(int w, int h, int nnodes) 497 | { 498 | FONSatlas* atlas = NULL; 499 | 500 | // Allocate memory for the font stash. 501 | atlas = (FONSatlas*)malloc(sizeof(FONSatlas)); 502 | if (atlas == NULL) goto error; 503 | memset(atlas, 0, sizeof(FONSatlas)); 504 | 505 | atlas->width = w; 506 | atlas->height = h; 507 | 508 | // Allocate space for skyline nodes 509 | atlas->nodes = (FONSatlasNode*)malloc(sizeof(FONSatlasNode) * nnodes); 510 | if (atlas->nodes == NULL) goto error; 511 | memset(atlas->nodes, 0, sizeof(FONSatlasNode) * nnodes); 512 | atlas->nnodes = 0; 513 | atlas->cnodes = nnodes; 514 | 515 | // Init root node. 516 | atlas->nodes[0].x = 0; 517 | atlas->nodes[0].y = 0; 518 | atlas->nodes[0].width = (short)w; 519 | atlas->nnodes++; 520 | 521 | return atlas; 522 | 523 | error: 524 | if (atlas) fons__deleteAtlas(atlas); 525 | return NULL; 526 | } 527 | 528 | static int fons__atlasInsertNode(FONSatlas* atlas, int idx, int x, int y, int w) 529 | { 530 | int i; 531 | // Insert node 532 | if (atlas->nnodes+1 > atlas->cnodes) { 533 | atlas->cnodes = atlas->cnodes == 0 ? 8 : atlas->cnodes * 2; 534 | atlas->nodes = (FONSatlasNode*)realloc(atlas->nodes, sizeof(FONSatlasNode) * atlas->cnodes); 535 | if (atlas->nodes == NULL) 536 | return 0; 537 | } 538 | for (i = atlas->nnodes; i > idx; i--) 539 | atlas->nodes[i] = atlas->nodes[i-1]; 540 | atlas->nodes[idx].x = (short)x; 541 | atlas->nodes[idx].y = (short)y; 542 | atlas->nodes[idx].width = (short)w; 543 | atlas->nnodes++; 544 | 545 | return 1; 546 | } 547 | 548 | static void fons__atlasRemoveNode(FONSatlas* atlas, int idx) 549 | { 550 | int i; 551 | if (atlas->nnodes == 0) return; 552 | for (i = idx; i < atlas->nnodes-1; i++) 553 | atlas->nodes[i] = atlas->nodes[i+1]; 554 | atlas->nnodes--; 555 | } 556 | 557 | static void fons__atlasExpand(FONSatlas* atlas, int w, int h) 558 | { 559 | // Insert node for empty space 560 | if (w > atlas->width) 561 | fons__atlasInsertNode(atlas, atlas->nnodes, atlas->width, 0, w - atlas->width); 562 | atlas->width = w; 563 | atlas->height = h; 564 | } 565 | 566 | static void fons__atlasReset(FONSatlas* atlas, int w, int h) 567 | { 568 | atlas->width = w; 569 | atlas->height = h; 570 | atlas->nnodes = 0; 571 | 572 | // Init root node. 573 | atlas->nodes[0].x = 0; 574 | atlas->nodes[0].y = 0; 575 | atlas->nodes[0].width = (short)w; 576 | atlas->nnodes++; 577 | } 578 | 579 | static int fons__atlasAddSkylineLevel(FONSatlas* atlas, int idx, int x, int y, int w, int h) 580 | { 581 | int i; 582 | 583 | // Insert new node 584 | if (fons__atlasInsertNode(atlas, idx, x, y+h, w) == 0) 585 | return 0; 586 | 587 | // Delete skyline segments that fall under the shaodw of the new segment. 588 | for (i = idx+1; i < atlas->nnodes; i++) { 589 | if (atlas->nodes[i].x < atlas->nodes[i-1].x + atlas->nodes[i-1].width) { 590 | int shrink = atlas->nodes[i-1].x + atlas->nodes[i-1].width - atlas->nodes[i].x; 591 | atlas->nodes[i].x += (short)shrink; 592 | atlas->nodes[i].width -= (short)shrink; 593 | if (atlas->nodes[i].width <= 0) { 594 | fons__atlasRemoveNode(atlas, i); 595 | i--; 596 | } else { 597 | break; 598 | } 599 | } else { 600 | break; 601 | } 602 | } 603 | 604 | // Merge same height skyline segments that are next to each other. 605 | for (i = 0; i < atlas->nnodes-1; i++) { 606 | if (atlas->nodes[i].y == atlas->nodes[i+1].y) { 607 | atlas->nodes[i].width += atlas->nodes[i+1].width; 608 | fons__atlasRemoveNode(atlas, i+1); 609 | i--; 610 | } 611 | } 612 | 613 | return 1; 614 | } 615 | 616 | static int fons__atlasRectFits(FONSatlas* atlas, int i, int w, int h) 617 | { 618 | // Checks if there is enough space at the location of skyline span 'i', 619 | // and return the max height of all skyline spans under that at that location, 620 | // (think tetris block being dropped at that position). Or -1 if no space found. 621 | int x = atlas->nodes[i].x; 622 | int y = atlas->nodes[i].y; 623 | int spaceLeft; 624 | if (x + w > atlas->width) 625 | return -1; 626 | spaceLeft = w; 627 | while (spaceLeft > 0) { 628 | if (i == atlas->nnodes) return -1; 629 | y = fons__maxi(y, atlas->nodes[i].y); 630 | if (y + h > atlas->height) return -1; 631 | spaceLeft -= atlas->nodes[i].width; 632 | ++i; 633 | } 634 | return y; 635 | } 636 | 637 | static int fons__atlasAddRect(FONSatlas* atlas, int rw, int rh, int* rx, int* ry) 638 | { 639 | int besth = atlas->height, bestw = atlas->width, besti = -1; 640 | int bestx = -1, besty = -1, i; 641 | 642 | // Bottom left fit heuristic. 643 | for (i = 0; i < atlas->nnodes; i++) { 644 | int y = fons__atlasRectFits(atlas, i, rw, rh); 645 | if (y != -1) { 646 | if (y + rh < besth || (y + rh == besth && atlas->nodes[i].width < bestw)) { 647 | besti = i; 648 | bestw = atlas->nodes[i].width; 649 | besth = y + rh; 650 | bestx = atlas->nodes[i].x; 651 | besty = y; 652 | } 653 | } 654 | } 655 | 656 | if (besti == -1) 657 | return 0; 658 | 659 | // Perform the actual packing. 660 | if (fons__atlasAddSkylineLevel(atlas, besti, bestx, besty, rw, rh) == 0) 661 | return 0; 662 | 663 | *rx = bestx; 664 | *ry = besty; 665 | 666 | return 1; 667 | } 668 | 669 | static void fons__addWhiteRect(FONScontext* stash, int w, int h) 670 | { 671 | int x, y, gx, gy; 672 | unsigned char* dst; 673 | if (fons__atlasAddRect(stash->atlas, w, h, &gx, &gy) == 0) 674 | return; 675 | 676 | // Rasterize 677 | dst = &stash->texData[gx + gy * stash->params.width]; 678 | for (y = 0; y < h; y++) { 679 | for (x = 0; x < w; x++) 680 | dst[x] = 0xff; 681 | dst += stash->params.width; 682 | } 683 | 684 | stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], gx); 685 | stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], gy); 686 | stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], gx+w); 687 | stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], gy+h); 688 | } 689 | 690 | FONScontext* fonsCreateInternal(FONSparams* params) 691 | { 692 | FONScontext* stash = NULL; 693 | 694 | // Allocate memory for the font stash. 695 | stash = (FONScontext*)malloc(sizeof(FONScontext)); 696 | if (stash == NULL) goto error; 697 | memset(stash, 0, sizeof(FONScontext)); 698 | 699 | stash->params = *params; 700 | 701 | // Allocate scratch buffer. 702 | stash->scratch = (unsigned char*)malloc(FONS_SCRATCH_BUF_SIZE); 703 | if (stash->scratch == NULL) goto error; 704 | 705 | // Initialize implementation library 706 | if (!fons__tt_init(stash)) goto error; 707 | 708 | if (stash->params.renderCreate != NULL) { 709 | if (stash->params.renderCreate(stash->params.userPtr, stash->params.width, stash->params.height) == 0) 710 | goto error; 711 | } 712 | 713 | stash->atlas = fons__allocAtlas(stash->params.width, stash->params.height, FONS_INIT_ATLAS_NODES); 714 | if (stash->atlas == NULL) goto error; 715 | 716 | // Allocate space for fonts. 717 | stash->fonts = (FONSfont**)malloc(sizeof(FONSfont*) * FONS_INIT_FONTS); 718 | if (stash->fonts == NULL) goto error; 719 | memset(stash->fonts, 0, sizeof(FONSfont*) * FONS_INIT_FONTS); 720 | stash->cfonts = FONS_INIT_FONTS; 721 | stash->nfonts = 0; 722 | 723 | // Create texture for the cache. 724 | stash->itw = 1.0f/stash->params.width; 725 | stash->ith = 1.0f/stash->params.height; 726 | stash->texData = (unsigned char*)malloc(stash->params.width * stash->params.height); 727 | if (stash->texData == NULL) goto error; 728 | memset(stash->texData, 0, stash->params.width * stash->params.height); 729 | 730 | stash->dirtyRect[0] = stash->params.width; 731 | stash->dirtyRect[1] = stash->params.height; 732 | stash->dirtyRect[2] = 0; 733 | stash->dirtyRect[3] = 0; 734 | 735 | // Add white rect at 0,0 for debug drawing. 736 | fons__addWhiteRect(stash, 2,2); 737 | 738 | fonsPushState(stash); 739 | fonsClearState(stash); 740 | 741 | return stash; 742 | 743 | error: 744 | fonsDeleteInternal(stash); 745 | return NULL; 746 | } 747 | 748 | static FONSstate* fons__getState(FONScontext* stash) 749 | { 750 | return &stash->states[stash->nstates-1]; 751 | } 752 | 753 | void fonsSetSize(FONScontext* stash, float size) 754 | { 755 | fons__getState(stash)->size = size; 756 | } 757 | 758 | void fonsSetColor(FONScontext* stash, unsigned int color) 759 | { 760 | fons__getState(stash)->color = color; 761 | } 762 | 763 | void fonsSetSpacing(FONScontext* stash, float spacing) 764 | { 765 | fons__getState(stash)->spacing = spacing; 766 | } 767 | 768 | void fonsSetBlur(FONScontext* stash, float blur) 769 | { 770 | fons__getState(stash)->blur = blur; 771 | } 772 | 773 | void fonsSetAlign(FONScontext* stash, int align) 774 | { 775 | fons__getState(stash)->align = align; 776 | } 777 | 778 | void fonsSetFont(FONScontext* stash, int font) 779 | { 780 | fons__getState(stash)->font = font; 781 | } 782 | 783 | void fonsPushState(FONScontext* stash) 784 | { 785 | if (stash->nstates >= FONS_MAX_STATES) { 786 | if (stash->handleError) 787 | stash->handleError(stash->errorUptr, FONS_STATES_OVERFLOW, 0); 788 | return; 789 | } 790 | if (stash->nstates > 0) 791 | memcpy(&stash->states[stash->nstates], &stash->states[stash->nstates-1], sizeof(FONSstate)); 792 | stash->nstates++; 793 | } 794 | 795 | void fonsPopState(FONScontext* stash) 796 | { 797 | if (stash->nstates <= 1) { 798 | if (stash->handleError) 799 | stash->handleError(stash->errorUptr, FONS_STATES_UNDERFLOW, 0); 800 | return; 801 | } 802 | stash->nstates--; 803 | } 804 | 805 | void fonsClearState(FONScontext* stash) 806 | { 807 | FONSstate* state = fons__getState(stash); 808 | state->size = 12.0f; 809 | state->color = 0xffffffff; 810 | state->font = 0; 811 | state->blur = 0; 812 | state->spacing = 0; 813 | state->align = FONS_ALIGN_LEFT | FONS_ALIGN_BASELINE; 814 | } 815 | 816 | static void fons__freeFont(FONSfont* font) 817 | { 818 | if (font == NULL) return; 819 | if (font->glyphs) free(font->glyphs); 820 | if (font->freeData && font->data) free(font->data); 821 | free(font); 822 | } 823 | 824 | static int fons__allocFont(FONScontext* stash) 825 | { 826 | FONSfont* font = NULL; 827 | if (stash->nfonts+1 > stash->cfonts) { 828 | stash->cfonts = stash->cfonts == 0 ? 8 : stash->cfonts * 2; 829 | stash->fonts = (FONSfont**)realloc(stash->fonts, sizeof(FONSfont*) * stash->cfonts); 830 | if (stash->fonts == NULL) 831 | return -1; 832 | } 833 | font = (FONSfont*)malloc(sizeof(FONSfont)); 834 | if (font == NULL) goto error; 835 | memset(font, 0, sizeof(FONSfont)); 836 | 837 | font->glyphs = (FONSglyph*)malloc(sizeof(FONSglyph) * FONS_INIT_GLYPHS); 838 | if (font->glyphs == NULL) goto error; 839 | font->cglyphs = FONS_INIT_GLYPHS; 840 | font->nglyphs = 0; 841 | 842 | stash->fonts[stash->nfonts++] = font; 843 | return stash->nfonts-1; 844 | 845 | error: 846 | fons__freeFont(font); 847 | 848 | return FONS_INVALID; 849 | } 850 | 851 | int fonsAddFont(FONScontext* stash, const char* name, const char* path) 852 | { 853 | FILE* fp = 0; 854 | int dataSize = 0; 855 | unsigned char* data = NULL; 856 | 857 | // Read in the font data. 858 | fp = fopen(path, "rb"); 859 | if (fp == NULL) goto error; 860 | fseek(fp,0,SEEK_END); 861 | dataSize = (int)ftell(fp); 862 | fseek(fp,0,SEEK_SET); 863 | data = (unsigned char*)malloc(dataSize); 864 | if (data == NULL) goto error; 865 | fread(data, 1, dataSize, fp); 866 | fclose(fp); 867 | fp = 0; 868 | 869 | return fonsAddFontMem(stash, name, data, dataSize, 1); 870 | 871 | error: 872 | if (data) free(data); 873 | if (fp) fclose(fp); 874 | return FONS_INVALID; 875 | } 876 | 877 | int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData) 878 | { 879 | int i, ascent, descent, fh, lineGap; 880 | FONSfont* font; 881 | 882 | int idx = fons__allocFont(stash); 883 | if (idx == FONS_INVALID) 884 | return FONS_INVALID; 885 | 886 | font = stash->fonts[idx]; 887 | 888 | strncpy(font->name, name, sizeof(font->name)); 889 | font->name[sizeof(font->name)-1] = '\0'; 890 | 891 | // Init hash lookup. 892 | for (i = 0; i < FONS_HASH_LUT_SIZE; ++i) 893 | font->lut[i] = -1; 894 | 895 | // Read in the font data. 896 | font->dataSize = dataSize; 897 | font->data = data; 898 | font->freeData = (unsigned char)freeData; 899 | 900 | // Init font 901 | stash->nscratch = 0; 902 | if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error; 903 | 904 | // Store normalized line height. The real line height is got 905 | // by multiplying the lineh by font size. 906 | fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); 907 | fh = ascent - descent; 908 | font->ascender = (float)ascent / (float)fh; 909 | font->descender = (float)descent / (float)fh; 910 | font->lineh = (float)(fh + lineGap) / (float)fh; 911 | 912 | return idx; 913 | 914 | error: 915 | fons__freeFont(font); 916 | stash->nfonts--; 917 | return FONS_INVALID; 918 | } 919 | 920 | int fonsGetFontByName(FONScontext* s, const char* name) 921 | { 922 | int i; 923 | for (i = 0; i < s->nfonts; i++) { 924 | if (strcmp(s->fonts[i]->name, name) == 0) 925 | return i; 926 | } 927 | return FONS_INVALID; 928 | } 929 | 930 | 931 | static FONSglyph* fons__allocGlyph(FONSfont* font) 932 | { 933 | if (font->nglyphs+1 > font->cglyphs) { 934 | font->cglyphs = font->cglyphs == 0 ? 8 : font->cglyphs * 2; 935 | font->glyphs = (FONSglyph*)realloc(font->glyphs, sizeof(FONSglyph) * font->cglyphs); 936 | if (font->glyphs == NULL) return NULL; 937 | } 938 | font->nglyphs++; 939 | return &font->glyphs[font->nglyphs-1]; 940 | } 941 | 942 | 943 | // Based on Exponential blur, Jani Huhtanen, 2006 944 | 945 | #define APREC 16 946 | #define ZPREC 7 947 | 948 | static void fons__blurCols(unsigned char* dst, int w, int h, int dstStride, int alpha) 949 | { 950 | int x, y; 951 | for (y = 0; y < h; y++) { 952 | int z = 0; // force zero border 953 | for (x = 1; x < w; x++) { 954 | z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC; 955 | dst[x] = (unsigned char)(z >> ZPREC); 956 | } 957 | dst[w-1] = 0; // force zero border 958 | z = 0; 959 | for (x = w-2; x >= 0; x--) { 960 | z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC; 961 | dst[x] = (unsigned char)(z >> ZPREC); 962 | } 963 | dst[0] = 0; // force zero border 964 | dst += dstStride; 965 | } 966 | } 967 | 968 | static void fons__blurRows(unsigned char* dst, int w, int h, int dstStride, int alpha) 969 | { 970 | int x, y; 971 | for (x = 0; x < w; x++) { 972 | int z = 0; // force zero border 973 | for (y = dstStride; y < h*dstStride; y += dstStride) { 974 | z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC; 975 | dst[y] = (unsigned char)(z >> ZPREC); 976 | } 977 | dst[(h-1)*dstStride] = 0; // force zero border 978 | z = 0; 979 | for (y = (h-2)*dstStride; y >= 0; y -= dstStride) { 980 | z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC; 981 | dst[y] = (unsigned char)(z >> ZPREC); 982 | } 983 | dst[0] = 0; // force zero border 984 | dst++; 985 | } 986 | } 987 | 988 | 989 | static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int dstStride, int blur) 990 | { 991 | int alpha; 992 | float sigma; 993 | (void)stash; 994 | 995 | if (blur < 1) 996 | return; 997 | // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) 998 | sigma = (float)blur * 0.57735f; // 1 / sqrt(3) 999 | alpha = (int)((1< 20) iblur = 20; 1022 | pad = iblur+2; 1023 | 1024 | // Reset allocator. 1025 | stash->nscratch = 0; 1026 | 1027 | // Find code point and size. 1028 | h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); 1029 | i = font->lut[h]; 1030 | while (i != -1) { 1031 | if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) 1032 | return &font->glyphs[i]; 1033 | i = font->glyphs[i].next; 1034 | } 1035 | 1036 | // Could not find glyph, create it. 1037 | scale = fons__tt_getPixelHeightScale(&font->font, size); 1038 | g = fons__tt_getGlyphIndex(&font->font, codepoint); 1039 | fons__tt_buildGlyphBitmap(&font->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); 1040 | gw = x1-x0 + pad*2; 1041 | gh = y1-y0 + pad*2; 1042 | 1043 | // Find free spot for the rect in the atlas 1044 | added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); 1045 | if (added == 0 && stash->handleError != NULL) { 1046 | // Atlas is full, let the user to resize the atlas (or not), and try again. 1047 | stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0); 1048 | added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); 1049 | } 1050 | if (added == 0) return NULL; 1051 | 1052 | // Init glyph. 1053 | glyph = fons__allocGlyph(font); 1054 | glyph->codepoint = codepoint; 1055 | glyph->size = isize; 1056 | glyph->blur = iblur; 1057 | glyph->index = g; 1058 | glyph->x0 = (short)gx; 1059 | glyph->y0 = (short)gy; 1060 | glyph->x1 = (short)(glyph->x0+gw); 1061 | glyph->y1 = (short)(glyph->y0+gh); 1062 | glyph->xadv = (short)(scale * advance * 10.0f); 1063 | glyph->xoff = (short)(x0 - pad); 1064 | glyph->yoff = (short)(y0 - pad); 1065 | glyph->next = 0; 1066 | 1067 | // Insert char to hash lookup. 1068 | glyph->next = font->lut[h]; 1069 | font->lut[h] = font->nglyphs-1; 1070 | 1071 | // Rasterize 1072 | dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width]; 1073 | fons__tt_renderGlyphBitmap(&font->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale,scale, g); 1074 | 1075 | // Make sure there is one pixel empty border. 1076 | dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; 1077 | for (y = 0; y < gh; y++) { 1078 | dst[y*stash->params.width] = 0; 1079 | dst[gw-1 + y*stash->params.width] = 0; 1080 | } 1081 | for (x = 0; x < gw; x++) { 1082 | dst[x] = 0; 1083 | dst[x + (gh-1)*stash->params.width] = 0; 1084 | } 1085 | 1086 | // Debug code to color the glyph background 1087 | /* unsigned char* fdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; 1088 | for (y = 0; y < gh; y++) { 1089 | for (x = 0; x < gw; x++) { 1090 | int a = (int)fdst[x+y*stash->params.width] + 20; 1091 | if (a > 255) a = 255; 1092 | fdst[x+y*stash->params.width] = a; 1093 | } 1094 | }*/ 1095 | 1096 | // Blur 1097 | if (iblur > 0) { 1098 | stash->nscratch = 0; 1099 | bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; 1100 | fons__blur(stash, bdst, gw,gh, stash->params.width, iblur); 1101 | } 1102 | 1103 | stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0); 1104 | stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], glyph->y0); 1105 | stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], glyph->x1); 1106 | stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], glyph->y1); 1107 | 1108 | return glyph; 1109 | } 1110 | 1111 | static void fons__getQuad(FONScontext* stash, FONSfont* font, 1112 | int prevGlyphIndex, FONSglyph* glyph, 1113 | float scale, float spacing, float* x, float* y, FONSquad* q) 1114 | { 1115 | float rx,ry,xoff,yoff,x0,y0,x1,y1; 1116 | 1117 | if (prevGlyphIndex != -1) { 1118 | float adv = fons__tt_getGlyphKernAdvance(&font->font, prevGlyphIndex, glyph->index) * scale; 1119 | *x += (int)(adv + spacing + 0.5f); 1120 | } 1121 | 1122 | // Each glyph has 2px border to allow good interpolation, 1123 | // one pixel to prevent leaking, and one to allow good interpolation for rendering. 1124 | // Inset the texture region by one pixel for corret interpolation. 1125 | xoff = (short)(glyph->xoff+1); 1126 | yoff = (short)(glyph->yoff+1); 1127 | x0 = (float)(glyph->x0+1); 1128 | y0 = (float)(glyph->y0+1); 1129 | x1 = (float)(glyph->x1-1); 1130 | y1 = (float)(glyph->y1-1); 1131 | 1132 | if (stash->params.flags & FONS_ZERO_TOPLEFT) { 1133 | rx = (float)(int)(*x + xoff); 1134 | ry = (float)(int)(*y + yoff); 1135 | 1136 | q->x0 = rx; 1137 | q->y0 = ry; 1138 | q->x1 = rx + x1 - x0; 1139 | q->y1 = ry + y1 - y0; 1140 | 1141 | q->s0 = x0 * stash->itw; 1142 | q->t0 = y0 * stash->ith; 1143 | q->s1 = x1 * stash->itw; 1144 | q->t1 = y1 * stash->ith; 1145 | } else { 1146 | rx = (float)(int)(*x + xoff); 1147 | ry = (float)(int)(*y - yoff); 1148 | 1149 | q->x0 = rx; 1150 | q->y0 = ry; 1151 | q->x1 = rx + x1 - x0; 1152 | q->y1 = ry - y1 + y0; 1153 | 1154 | q->s0 = x0 * stash->itw; 1155 | q->t0 = y0 * stash->ith; 1156 | q->s1 = x1 * stash->itw; 1157 | q->t1 = y1 * stash->ith; 1158 | } 1159 | 1160 | *x += (int)(glyph->xadv / 10.0f + 0.5f); 1161 | } 1162 | 1163 | static void fons__flush(FONScontext* stash) 1164 | { 1165 | // Flush texture 1166 | if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) { 1167 | if (stash->params.renderUpdate != NULL) 1168 | stash->params.renderUpdate(stash->params.userPtr, stash->dirtyRect, stash->texData); 1169 | // Reset dirty rect 1170 | stash->dirtyRect[0] = stash->params.width; 1171 | stash->dirtyRect[1] = stash->params.height; 1172 | stash->dirtyRect[2] = 0; 1173 | stash->dirtyRect[3] = 0; 1174 | } 1175 | 1176 | // Flush triangles 1177 | if (stash->nverts > 0) { 1178 | if (stash->params.renderDraw != NULL) 1179 | stash->params.renderDraw(stash->params.userPtr, stash->verts, stash->tcoords, stash->colors, stash->nverts); 1180 | stash->nverts = 0; 1181 | } 1182 | } 1183 | 1184 | static __inline void fons__vertex(FONScontext* stash, float x, float y, float s, float t, unsigned int c) 1185 | { 1186 | stash->verts[stash->nverts*2+0] = x; 1187 | stash->verts[stash->nverts*2+1] = y; 1188 | stash->tcoords[stash->nverts*2+0] = s; 1189 | stash->tcoords[stash->nverts*2+1] = t; 1190 | stash->colors[stash->nverts] = c; 1191 | stash->nverts++; 1192 | } 1193 | 1194 | static float fons__getVertAlign(FONScontext* stash, FONSfont* font, int align, short isize) 1195 | { 1196 | if (stash->params.flags & FONS_ZERO_TOPLEFT) { 1197 | if (align & FONS_ALIGN_TOP) { 1198 | return font->ascender * (float)isize/10.0f; 1199 | } else if (align & FONS_ALIGN_MIDDLE) { 1200 | return (font->ascender + font->descender) / 2.0f * (float)isize/10.0f; 1201 | } else if (align & FONS_ALIGN_BASELINE) { 1202 | return 0.0f; 1203 | } else if (align & FONS_ALIGN_BOTTOM) { 1204 | return font->descender * (float)isize/10.0f; 1205 | } 1206 | } else { 1207 | if (align & FONS_ALIGN_TOP) { 1208 | return -font->ascender * (float)isize/10.0f; 1209 | } else if (align & FONS_ALIGN_MIDDLE) { 1210 | return -(font->ascender + font->descender) / 2.0f * (float)isize/10.0f; 1211 | } else if (align & FONS_ALIGN_BASELINE) { 1212 | return 0.0f; 1213 | } else if (align & FONS_ALIGN_BOTTOM) { 1214 | return -font->descender * (float)isize/10.0f; 1215 | } 1216 | } 1217 | return 0.0; 1218 | } 1219 | 1220 | float fonsDrawText(FONScontext* stash, 1221 | float x, float y, 1222 | const char* str, const char* end) 1223 | { 1224 | FONSstate* state = fons__getState(stash); 1225 | unsigned int codepoint; 1226 | unsigned int utf8state = 0; 1227 | FONSglyph* glyph = NULL; 1228 | FONSquad q; 1229 | int prevGlyphIndex = -1; 1230 | short isize = (short)(state->size*10.0f); 1231 | short iblur = (short)state->blur; 1232 | float scale; 1233 | FONSfont* font; 1234 | float width; 1235 | 1236 | if (stash == NULL) return x; 1237 | if (state->font < 0 || state->font >= stash->nfonts) return x; 1238 | font = stash->fonts[state->font]; 1239 | if (font->data == NULL) return x; 1240 | 1241 | scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); 1242 | 1243 | if (end == NULL) 1244 | end = str + strlen(str); 1245 | 1246 | // Align horizontally 1247 | if (state->align & FONS_ALIGN_LEFT) { 1248 | // empty 1249 | } else if (state->align & FONS_ALIGN_RIGHT) { 1250 | width = fonsTextBounds(stash, x,y, str, end, NULL); 1251 | x -= width; 1252 | } else if (state->align & FONS_ALIGN_CENTER) { 1253 | width = fonsTextBounds(stash, x,y, str, end, NULL); 1254 | x -= width * 0.5f; 1255 | } 1256 | // Align vertically. 1257 | y += fons__getVertAlign(stash, font, state->align, isize); 1258 | 1259 | for (; str != end; ++str) { 1260 | if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) 1261 | continue; 1262 | glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); 1263 | if (glyph != NULL) { 1264 | fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); 1265 | 1266 | if (stash->nverts+6 > FONS_VERTEX_COUNT) 1267 | fons__flush(stash); 1268 | 1269 | fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color); 1270 | fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color); 1271 | fons__vertex(stash, q.x1, q.y0, q.s1, q.t0, state->color); 1272 | 1273 | fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color); 1274 | fons__vertex(stash, q.x0, q.y1, q.s0, q.t1, state->color); 1275 | fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color); 1276 | } 1277 | prevGlyphIndex = glyph != NULL ? glyph->index : -1; 1278 | } 1279 | fons__flush(stash); 1280 | 1281 | return x; 1282 | } 1283 | 1284 | int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, 1285 | float x, float y, const char* str, const char* end) 1286 | { 1287 | FONSstate* state = fons__getState(stash); 1288 | float width; 1289 | 1290 | memset(iter, 0, sizeof(*iter)); 1291 | 1292 | if (stash == NULL) return 0; 1293 | if (state->font < 0 || state->font >= stash->nfonts) return 0; 1294 | iter->font = stash->fonts[state->font]; 1295 | if (iter->font->data == NULL) return 0; 1296 | 1297 | iter->isize = (short)(state->size*10.0f); 1298 | iter->iblur = (short)state->blur; 1299 | iter->scale = fons__tt_getPixelHeightScale(&iter->font->font, (float)iter->isize/10.0f); 1300 | 1301 | // Align horizontally 1302 | if (state->align & FONS_ALIGN_LEFT) { 1303 | // empty 1304 | } else if (state->align & FONS_ALIGN_RIGHT) { 1305 | width = fonsTextBounds(stash, x,y, str, end, NULL); 1306 | x -= width; 1307 | } else if (state->align & FONS_ALIGN_CENTER) { 1308 | width = fonsTextBounds(stash, x,y, str, end, NULL); 1309 | x -= width * 0.5f; 1310 | } 1311 | // Align vertically. 1312 | y += fons__getVertAlign(stash, iter->font, state->align, iter->isize); 1313 | 1314 | if (end == NULL) 1315 | end = str + strlen(str); 1316 | 1317 | iter->x = iter->nextx = x; 1318 | iter->y = iter->nexty = y; 1319 | iter->spacing = state->spacing; 1320 | iter->str = str; 1321 | iter->next = str; 1322 | iter->end = end; 1323 | iter->codepoint = 0; 1324 | iter->prevGlyphIndex = -1; 1325 | 1326 | return 1; 1327 | } 1328 | 1329 | int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad) 1330 | { 1331 | FONSglyph* glyph = NULL; 1332 | const char* str = iter->next; 1333 | iter->str = iter->next; 1334 | 1335 | if (str == iter->end) 1336 | return 0; 1337 | 1338 | for (; str != iter->end; str++) { 1339 | if (fons__decutf8(&iter->utf8state, &iter->codepoint, *(const unsigned char*)str)) 1340 | continue; 1341 | str++; 1342 | // Get glyph and quad 1343 | iter->x = iter->nextx; 1344 | iter->y = iter->nexty; 1345 | glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur); 1346 | if (glyph != NULL) 1347 | fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); 1348 | iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1; 1349 | break; 1350 | } 1351 | iter->next = str; 1352 | 1353 | return 1; 1354 | } 1355 | 1356 | void fonsDrawDebug(FONScontext* stash, float x, float y) 1357 | { 1358 | int i; 1359 | int w = stash->params.width; 1360 | int h = stash->params.height; 1361 | float u = w == 0 ? 0 : (1.0f / w); 1362 | float v = h == 0 ? 0 : (1.0f / h); 1363 | 1364 | if (stash->nverts+6+6 > FONS_VERTEX_COUNT) 1365 | fons__flush(stash); 1366 | 1367 | // Draw background 1368 | fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff); 1369 | fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff); 1370 | fons__vertex(stash, x+w, y+0, u, v, 0x0fffffff); 1371 | 1372 | fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff); 1373 | fons__vertex(stash, x+0, y+h, u, v, 0x0fffffff); 1374 | fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff); 1375 | 1376 | // Draw texture 1377 | fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); 1378 | fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); 1379 | fons__vertex(stash, x+w, y+0, 1, 0, 0xffffffff); 1380 | 1381 | fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); 1382 | fons__vertex(stash, x+0, y+h, 0, 1, 0xffffffff); 1383 | fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); 1384 | 1385 | // Drawbug draw atlas 1386 | for (i = 0; i < stash->atlas->nnodes; i++) { 1387 | FONSatlasNode* n = &stash->atlas->nodes[i]; 1388 | 1389 | if (stash->nverts+6 > FONS_VERTEX_COUNT) 1390 | fons__flush(stash); 1391 | 1392 | fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff); 1393 | fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff); 1394 | fons__vertex(stash, x+n->x+n->width, y+n->y+0, u, v, 0xc00000ff); 1395 | 1396 | fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff); 1397 | fons__vertex(stash, x+n->x+0, y+n->y+1, u, v, 0xc00000ff); 1398 | fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff); 1399 | } 1400 | 1401 | fons__flush(stash); 1402 | } 1403 | 1404 | float fonsTextBounds(FONScontext* stash, 1405 | float x, float y, 1406 | const char* str, const char* end, 1407 | float* bounds) 1408 | { 1409 | FONSstate* state = fons__getState(stash); 1410 | unsigned int codepoint; 1411 | unsigned int utf8state = 0; 1412 | FONSquad q; 1413 | FONSglyph* glyph = NULL; 1414 | int prevGlyphIndex = -1; 1415 | short isize = (short)(state->size*10.0f); 1416 | short iblur = (short)state->blur; 1417 | float scale; 1418 | FONSfont* font; 1419 | float startx, advance; 1420 | float minx, miny, maxx, maxy; 1421 | 1422 | if (stash == NULL) return 0; 1423 | if (state->font < 0 || state->font >= stash->nfonts) return 0; 1424 | font = stash->fonts[state->font]; 1425 | if (font->data == NULL) return 0; 1426 | 1427 | scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); 1428 | 1429 | // Align vertically. 1430 | y += fons__getVertAlign(stash, font, state->align, isize); 1431 | 1432 | minx = maxx = x; 1433 | miny = maxy = y; 1434 | startx = x; 1435 | 1436 | if (end == NULL) 1437 | end = str + strlen(str); 1438 | 1439 | for (; str != end; ++str) { 1440 | if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) 1441 | continue; 1442 | glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); 1443 | if (glyph != NULL) { 1444 | fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); 1445 | if (q.x0 < minx) minx = q.x0; 1446 | if (q.x1 > maxx) maxx = q.x1; 1447 | if (stash->params.flags & FONS_ZERO_TOPLEFT) { 1448 | if (q.y0 < miny) miny = q.y0; 1449 | if (q.y1 > maxy) maxy = q.y1; 1450 | } else { 1451 | if (q.y1 < miny) miny = q.y1; 1452 | if (q.y0 > maxy) maxy = q.y0; 1453 | } 1454 | } 1455 | prevGlyphIndex = glyph != NULL ? glyph->index : -1; 1456 | } 1457 | 1458 | advance = x - startx; 1459 | 1460 | // Align horizontally 1461 | if (state->align & FONS_ALIGN_LEFT) { 1462 | // empty 1463 | } else if (state->align & FONS_ALIGN_RIGHT) { 1464 | minx -= advance; 1465 | maxx -= advance; 1466 | } else if (state->align & FONS_ALIGN_CENTER) { 1467 | minx -= advance * 0.5f; 1468 | maxx -= advance * 0.5f; 1469 | } 1470 | 1471 | if (bounds) { 1472 | bounds[0] = minx; 1473 | bounds[1] = miny; 1474 | bounds[2] = maxx; 1475 | bounds[3] = maxy; 1476 | } 1477 | 1478 | return advance; 1479 | } 1480 | 1481 | void fonsVertMetrics(FONScontext* stash, 1482 | float* ascender, float* descender, float* lineh) 1483 | { 1484 | FONSfont* font; 1485 | FONSstate* state = fons__getState(stash); 1486 | short isize; 1487 | 1488 | if (stash == NULL) return; 1489 | if (state->font < 0 || state->font >= stash->nfonts) return; 1490 | font = stash->fonts[state->font]; 1491 | isize = (short)(state->size*10.0f); 1492 | if (font->data == NULL) return; 1493 | 1494 | if (ascender) 1495 | *ascender = font->ascender*isize/10.0f; 1496 | if (descender) 1497 | *descender = font->descender*isize/10.0f; 1498 | if (lineh) 1499 | *lineh = font->lineh*isize/10.0f; 1500 | } 1501 | 1502 | void fonsLineBounds(FONScontext* stash, float y, float* miny, float* maxy) 1503 | { 1504 | FONSfont* font; 1505 | FONSstate* state = fons__getState(stash); 1506 | short isize; 1507 | 1508 | if (stash == NULL) return; 1509 | if (state->font < 0 || state->font >= stash->nfonts) return; 1510 | font = stash->fonts[state->font]; 1511 | isize = (short)(state->size*10.0f); 1512 | if (font->data == NULL) return; 1513 | 1514 | y += fons__getVertAlign(stash, font, state->align, isize); 1515 | 1516 | if (stash->params.flags & FONS_ZERO_TOPLEFT) { 1517 | *miny = y - font->ascender * (float)isize/10.0f; 1518 | *maxy = *miny + font->lineh*isize/10.0f; 1519 | } else { 1520 | *maxy = y + font->descender * (float)isize/10.0f; 1521 | *miny = *maxy - font->lineh*isize/10.0f; 1522 | } 1523 | } 1524 | 1525 | const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height) 1526 | { 1527 | if (width != NULL) 1528 | *width = stash->params.width; 1529 | if (height != NULL) 1530 | *height = stash->params.height; 1531 | return stash->texData; 1532 | } 1533 | 1534 | int fonsValidateTexture(FONScontext* stash, int* dirty) 1535 | { 1536 | if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) { 1537 | dirty[0] = stash->dirtyRect[0]; 1538 | dirty[1] = stash->dirtyRect[1]; 1539 | dirty[2] = stash->dirtyRect[2]; 1540 | dirty[3] = stash->dirtyRect[3]; 1541 | // Reset dirty rect 1542 | stash->dirtyRect[0] = stash->params.width; 1543 | stash->dirtyRect[1] = stash->params.height; 1544 | stash->dirtyRect[2] = 0; 1545 | stash->dirtyRect[3] = 0; 1546 | return 1; 1547 | } 1548 | return 0; 1549 | } 1550 | 1551 | void fonsDeleteInternal(FONScontext* stash) 1552 | { 1553 | int i; 1554 | if (stash == NULL) return; 1555 | 1556 | if (stash->params.renderDelete) 1557 | stash->params.renderDelete(stash->params.userPtr); 1558 | 1559 | for (i = 0; i < stash->nfonts; ++i) 1560 | fons__freeFont(stash->fonts[i]); 1561 | 1562 | if (stash->atlas) fons__deleteAtlas(stash->atlas); 1563 | if (stash->fonts) free(stash->fonts); 1564 | if (stash->texData) free(stash->texData); 1565 | if (stash->scratch) free(stash->scratch); 1566 | free(stash); 1567 | } 1568 | 1569 | void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) 1570 | { 1571 | if (stash == NULL) return; 1572 | stash->handleError = callback; 1573 | stash->errorUptr = uptr; 1574 | } 1575 | 1576 | void fonsGetAtlasSize(FONScontext* stash, int* width, int* height) 1577 | { 1578 | if (stash == NULL) return; 1579 | *width = stash->params.width; 1580 | *height = stash->params.height; 1581 | } 1582 | 1583 | int fonsExpandAtlas(FONScontext* stash, int width, int height) 1584 | { 1585 | int i, maxy = 0; 1586 | unsigned char* data = NULL; 1587 | if (stash == NULL) return 0; 1588 | 1589 | width = fons__maxi(width, stash->params.width); 1590 | height = fons__maxi(height, stash->params.height); 1591 | 1592 | if (width == stash->params.width && height == stash->params.height) 1593 | return 1; 1594 | 1595 | // Flush pending glyphs. 1596 | fons__flush(stash); 1597 | 1598 | // Create new texture 1599 | if (stash->params.renderResize != NULL) { 1600 | if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) 1601 | return 0; 1602 | } 1603 | // Copy old texture data over. 1604 | data = (unsigned char*)malloc(width * height); 1605 | if (data == NULL) 1606 | return 0; 1607 | for (i = 0; i < stash->params.height; i++) { 1608 | unsigned char* dst = &data[i*width]; 1609 | unsigned char* src = &stash->texData[i*stash->params.width]; 1610 | memcpy(dst, src, stash->params.width); 1611 | if (width > stash->params.width) 1612 | memset(dst+stash->params.width, 0, width - stash->params.width); 1613 | } 1614 | if (height > stash->params.height) 1615 | memset(&data[stash->params.height * width], 0, (height - stash->params.height) * width); 1616 | 1617 | free(stash->texData); 1618 | stash->texData = data; 1619 | 1620 | // Increase atlas size 1621 | fons__atlasExpand(stash->atlas, width, height); 1622 | 1623 | // Add axisting data as dirty. 1624 | for (i = 0; i < stash->atlas->nnodes; i++) 1625 | maxy = fons__maxi(maxy, stash->atlas->nodes[i].y); 1626 | stash->dirtyRect[0] = 0; 1627 | stash->dirtyRect[1] = 0; 1628 | stash->dirtyRect[2] = stash->params.width; 1629 | stash->dirtyRect[3] = maxy; 1630 | 1631 | stash->params.width = width; 1632 | stash->params.height = height; 1633 | stash->itw = 1.0f/stash->params.width; 1634 | stash->ith = 1.0f/stash->params.height; 1635 | 1636 | return 1; 1637 | } 1638 | 1639 | int fonsResetAtlas(FONScontext* stash, int width, int height) 1640 | { 1641 | int i, j; 1642 | if (stash == NULL) return 0; 1643 | 1644 | // Flush pending glyphs. 1645 | fons__flush(stash); 1646 | 1647 | // Create new texture 1648 | if (stash->params.renderResize != NULL) { 1649 | if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) 1650 | return 0; 1651 | } 1652 | 1653 | // Reset atlas 1654 | fons__atlasReset(stash->atlas, width, height); 1655 | 1656 | // Clear texture data. 1657 | stash->texData = (unsigned char*)realloc(stash->texData, width * height); 1658 | if (stash->texData == NULL) return 0; 1659 | memset(stash->texData, 0, width * height); 1660 | 1661 | // Reset dirty rect 1662 | stash->dirtyRect[0] = width; 1663 | stash->dirtyRect[1] = height; 1664 | stash->dirtyRect[2] = 0; 1665 | stash->dirtyRect[3] = 0; 1666 | 1667 | // Reset cached glyphs 1668 | for (i = 0; i < stash->nfonts; i++) { 1669 | FONSfont* font = stash->fonts[i]; 1670 | font->nglyphs = 0; 1671 | for (j = 0; j < FONS_HASH_LUT_SIZE; j++) 1672 | font->lut[j] = -1; 1673 | } 1674 | 1675 | stash->params.width = width; 1676 | stash->params.height = height; 1677 | stash->itw = 1.0f/stash->params.width; 1678 | stash->ith = 1.0f/stash->params.height; 1679 | 1680 | // Add white rect at 0,0 for debug drawing. 1681 | fons__addWhiteRect(stash, 2,2); 1682 | 1683 | return 1; 1684 | } 1685 | 1686 | 1687 | #endif 1688 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014 Shinya Miyamoto( smiyaxdev@gmail.com ) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | 27 | #ifdef NANOVG_GLEW 28 | #define GLEW_STATIC 29 | #include 30 | #else 31 | #define GL_GLEXT_PROTOTYPES 32 | #endif 33 | 34 | #define GLFW_INCLUDE_GLCOREARB 35 | #include 36 | #include "nanovg.h" 37 | #include "nanovg_gl.h" 38 | #include "nanovg_gl_utils.h" 39 | 40 | #include "nanoui.h" 41 | 42 | 43 | using namespace nanoui; 44 | 45 | void errorcb(int error, const char* desc) 46 | { 47 | printf("GLFW error %d: %s\n", error, desc); 48 | } 49 | 50 | static void keycb(GLFWwindow* window, int key, int scancode, int action, int mods) 51 | { 52 | } 53 | 54 | static void charcb(GLFWwindow* window, unsigned int codepoint) 55 | { 56 | } 57 | 58 | eBtnState g_btn=btnOFF; 59 | 60 | static void buttoncb(GLFWwindow* window, int button, int action, int mods) 61 | { 62 | if (button == GLFW_MOUSE_BUTTON_LEFT ) { 63 | if (action == GLFW_PRESS) { 64 | g_btn = btnON; 65 | } 66 | if (action == GLFW_RELEASE) { 67 | g_btn = btnOFF; 68 | } 69 | } 70 | } 71 | 72 | class AnimateButton : public Button 73 | { 74 | protected: 75 | float scale; 76 | bool running; 77 | bool repeat; 78 | int atime; 79 | public: 80 | AnimateButton( const char * name , const char * title, int x, int y, int width, int height ) 81 | { 82 | scale = 1.0f; 83 | repeat = true; 84 | this->name = name; 85 | this->title = title; 86 | pos.x = x; 87 | pos.y = y; 88 | size.w = width; 89 | size.h = height; 90 | running = false; 91 | atime=-1; 92 | } 93 | 94 | virtual bool onFrameMove( Screen * sp, int time ) 95 | { 96 | Matrix4x4 mtx; 97 | 98 | if( running == false ) 99 | { 100 | return true; 101 | } 102 | 103 | if( atime == -1 ) 104 | { 105 | atime=time; 106 | } 107 | 108 | float t = (float)((time-atime)%1000)/1000.0f; 109 | scale = 1.0f + (sin(t*M_PI*2.0)+1.0f)*0.10; 110 | 111 | //printf("time:%d,%f\n",time,scale); 112 | 113 | mtx.translate( ((size.w/2)),((size.h/2)) , 0.0f ); 114 | mtx.scale( scale, scale, 1.0f ); 115 | mtx.translate( -((size.w/2)),-((size.h/2)) , 0.0f ); 116 | this->animetion_mtx = mtx; 117 | 118 | if( scale == 1.0f && repeat == false ) 119 | { 120 | running = false; 121 | } 122 | 123 | return true; 124 | } 125 | 126 | virtual void onHoverCursol( int x, int y) 127 | { 128 | running = true; 129 | repeat = true; 130 | atime = -1; 131 | Button::onHoverCursol(x,y); 132 | } 133 | 134 | virtual void onLeaveCursol() 135 | { 136 | repeat = false; 137 | Button::onLeaveCursol(); 138 | } 139 | 140 | }; 141 | 142 | class BigButton : public Button 143 | { 144 | protected: 145 | float scale; 146 | int animestate; 147 | bool repeat; 148 | int atime; 149 | float duration; 150 | float targesw; 151 | float targesh; 152 | float targesx; 153 | float targesy; 154 | float t; 155 | public: 156 | BigButton( const char * name , const char * title, int x, int y, int width, int height ) 157 | { 158 | scale = 1.0f; 159 | repeat = true; 160 | this->name = name; 161 | this->title = title; 162 | pos.x = x; 163 | pos.y = y; 164 | size.w = width; 165 | size.h = height; 166 | atime=-1; 167 | duration = 1000.0f; 168 | animestate = -1; 169 | 170 | } 171 | 172 | virtual bool onFrameMove( Screen * sp, int time ) 173 | { 174 | Matrix4x4 mtx; 175 | 176 | if( animestate == -1 ) 177 | { 178 | return Button::onFrameMove(sp, time); 179 | } 180 | 181 | invalid = true; 182 | 183 | if( atime == -1 ) 184 | { 185 | targesw = sp->getWidth() / (float)size.w; 186 | targesh = sp->getHeight() / (float)size.h; 187 | targesx = sp->getWidth() / 2.0f - apos.x -size.w/2.0; 188 | targesy = sp->getHeight() / 2.0f - apos.y -size.h/2.0; 189 | atime=time; 190 | } 191 | 192 | if( animestate == 0 ) 193 | { 194 | float pos = (float)(time-atime)/(float)duration; 195 | float cx = 1.0f + targesw * easeInOutQuint(pos); 196 | float cy = 1.0f + targesh * easeInOutQuint(pos); 197 | float px = targesx * easeInOutQuint(pos); 198 | float py = targesy * easeInOutQuint(pos); 199 | //printf("time:%d,%f\n",time,scale); 200 | 201 | mtx.translate( px,py , 0.0f ); 202 | mtx.translate( ((size.w/2)),((size.h/2)) , 0.0f ); 203 | mtx.scale( cx, cy, 1.0f ); 204 | mtx.translate( -((size.w/2)),-((size.h/2)) , 0.0f ); 205 | 206 | this->animetion_mtx = mtx; 207 | 208 | if( pos >= 1.0f ) 209 | { 210 | animestate = 1; 211 | atime = -1; 212 | } 213 | 214 | }else if( animestate == 1 ) 215 | { 216 | float pos = (float)(time-atime)/(float)(duration*5); 217 | 218 | t = (float)(time-atime)/(float)duration; 219 | if( pos >= 1.0f ) 220 | { 221 | animestate = 2; 222 | atime = -1; 223 | } 224 | }else if( animestate == 2 ) 225 | { 226 | float pos = (float)(time-atime)/(float)duration; 227 | float cx = 1.0f + targesw * ( 1.0f - easeInOutQuint(pos)); 228 | float cy = 1.0f + targesh * ( 1.0f - easeInOutQuint(pos)); 229 | float px = targesx * ( 1.0f - easeInOutQuint(pos)); 230 | float py = targesy * ( 1.0f - easeInOutQuint(pos)); 231 | //printf("time:%d,%f\n",time,scale); 232 | 233 | mtx.translate( px,py , 0.0f ); 234 | 235 | mtx.translate( ((size.w/2)),((size.h/2)) , 0.0f ); 236 | mtx.scale( cx, cy, 1.0f ); 237 | mtx.translate( -((size.w/2)),-((size.h/2)) , 0.0f ); 238 | 239 | this->animetion_mtx = mtx; 240 | 241 | 242 | if( pos >= 1.0f ) 243 | { 244 | animestate = -1; 245 | atime = -1; 246 | this->pos.z = 0.0f; 247 | } 248 | 249 | } 250 | 251 | return Button::onFrameMove(sp,time); 252 | } 253 | 254 | virtual void draw( Screen * sp, NVGcontext* vg ) 255 | { 256 | Button::draw( sp, vg ); 257 | if( animestate == 1 ) 258 | { 259 | float cx = sp->getWidth()/2; 260 | float cy = sp->getHeight()/2; 261 | float r = 72; 262 | float a0 = 0.0f + t*6; 263 | float a1 = NVG_PI + t*6; 264 | float r0 = r; 265 | float r1 = r * 0.75f; 266 | float ax,ay, bx,by; 267 | NVGpaint paint; 268 | 269 | nvgSave(vg); 270 | nvgReset(vg); 271 | nvgBeginPath(vg); 272 | nvgArc(vg, cx,cy, r0, a0, a1, NVG_CW); 273 | nvgArc(vg, cx,cy, r1, a1, a0, NVG_CCW); 274 | nvgClosePath(vg); 275 | ax = cx + cosf(a0) * (r0+r1)*0.5f; 276 | ay = cy + sinf(a0) * (r0+r1)*0.5f; 277 | bx = cx + cosf(a1) * (r0+r1)*0.5f; 278 | by = cy + sinf(a1) * (r0+r1)*0.5f; 279 | paint = nvgLinearGradient(vg, ax,ay, bx,by, nvgRGBA(236,239,241,0), nvgRGBA(236,239,241,255)); 280 | nvgFillPaint(vg, paint); 281 | nvgFill(vg); 282 | nvgRestore(vg); 283 | } 284 | 285 | } 286 | 287 | virtual void onClick( ) 288 | { 289 | atime = -1; 290 | repeat = false; 291 | title = ""; 292 | animestate = 0; 293 | this->pos.z = -1.0; 294 | 295 | } 296 | }; 297 | 298 | class BigButton2 : public Button 299 | { 300 | protected: 301 | int animestate; 302 | int starttime; 303 | int duration; 304 | float aval; 305 | float alpha; 306 | int cx; 307 | int cy; 308 | float r; 309 | 310 | public: 311 | BigButton2( const char * name , const char * title, int x, int y, int width, int height ) 312 | { 313 | animestate = 0; 314 | starttime = -1; 315 | duration = 1000*2; 316 | aval = 0.0f; 317 | alpha = 0.0f; 318 | r = -1.0f; 319 | 320 | this->name = name; 321 | this->title = title; 322 | pos.x = x; 323 | pos.y = y; 324 | size.w = width; 325 | size.h = height; 326 | 327 | hovering = false; 328 | atime = -1; 329 | scale = 0.0f; 330 | } 331 | 332 | virtual bool onFrameMove( Screen * sp, int time ) 333 | { 334 | // Animation 335 | if( animestate == 1 ) 336 | { 337 | if(starttime == -1 ) 338 | { 339 | starttime = time; 340 | } 341 | 342 | if( r < 0 ) 343 | { 344 | r = sqrt( (double)sp->getWidth() * (double)sp->getWidth() 345 | + (double)sp->getHeight() * (double)sp->getHeight()); 346 | } 347 | 348 | float pos = (float)(time-starttime)/(float)duration; 349 | aval = r * ( easeInOutQuint(pos)); 350 | alpha = easeOutQuint(pos); 351 | 352 | if( pos > 1.0f ) 353 | { 354 | pos = 0.0f; 355 | animestate = 2; 356 | invalid = false; 357 | starttime = -1; 358 | }else{ 359 | invalid = true; 360 | } 361 | // Keep 362 | }else if( animestate == 2 ) 363 | { 364 | if(starttime == -1 ) 365 | { 366 | starttime = time; 367 | } 368 | 369 | float pos = (float)(time-starttime)/(float)duration; 370 | aval = r; //* ( easeInOutQuint(pos)); 371 | 372 | if( pos > 1.0f ) 373 | { 374 | pos = 0.0f; 375 | animestate = 3; 376 | invalid = false; 377 | starttime = -1; 378 | }else{ 379 | invalid = true; 380 | } 381 | // Back 382 | }else if( animestate == 3 ) 383 | { 384 | if(starttime == -1 ) 385 | { 386 | starttime = time; 387 | } 388 | 389 | float pos = (float)(time-starttime)/(float)duration; 390 | aval = r * ( 1.0f - easeInOutQuint(pos)); 391 | alpha = 1.0f - easeInQuint(pos); 392 | 393 | if( pos > 1.0f ) 394 | { 395 | pos = 0.0f; 396 | animestate = 0; 397 | invalid = false; 398 | starttime = -1; 399 | }else{ 400 | invalid = true; 401 | this->pos.z = 0.0f; 402 | } 403 | } 404 | 405 | return Button::onFrameMove(sp,time); 406 | } 407 | 408 | virtual void draw( Screen * sp, NVGcontext* vg ) 409 | { 410 | Button::draw(sp,vg); 411 | if( aval > 0.0f ){ 412 | 413 | Matrix4x4 tmtx; 414 | nvgSave(vg); 415 | float m[6]; 416 | tmtx = matrix * animetion_mtx; 417 | tmtx.getMatrix2x3( m ); 418 | nvgTransform( vg, m[0],m[1],m[2],m[3],m[4],m[5] ); 419 | 420 | nvgBeginPath(vg); 421 | nvgCircle(vg, cx, cy, aval ); 422 | nvgFillColor(vg, nvgRGBA(0,96,128,255*alpha)); 423 | nvgFill(vg); 424 | nvgRestore(vg); 425 | 426 | nvgSave(vg); 427 | nvgReset(vg); 428 | nvgFontSize(vg, 72.0f); 429 | nvgFontFace(vg, "sans"); 430 | nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE); 431 | 432 | nvgFillColor(vg, nvgRGBA(0,0,0,160*(aval/r))); 433 | nvgText(vg, sp->getWidth()/2 * (aval/r) ,sp->getHeight()/2 -1 ,"Searching...", NULL); 434 | 435 | nvgFillColor(vg, nvgRGBA(255,255,255,255*(aval/1200.0f))); 436 | nvgText(vg, sp->getWidth()/2 * (aval/r) ,sp->getHeight()/2,"Searching...", NULL); 437 | nvgRestore(vg); 438 | 439 | } 440 | } 441 | 442 | virtual void onClick( ) 443 | { 444 | this->cx = size.w/2; 445 | this->cy = size.h/2; 446 | this->animestate = 1; 447 | this->pos.z = -1.0; 448 | } 449 | 450 | 451 | }; 452 | 453 | class NanoUiTest : public Screen 454 | { 455 | 456 | public: 457 | NanoUiTest( ) 458 | { 459 | 460 | } 461 | 462 | struct OnButtonXYcbk : public EventCallBack 463 | { 464 | NanoUiTest * parent; 465 | OnButtonXYcbk( NanoUiTest * parent ){ this->parent = parent; } 466 | virtual void operator()(UiEvent * p ) 467 | { 468 | 469 | UiEventXY * pxy = dynamic_cast(p); 470 | if(pxy) 471 | { 472 | printf("OnButtonXY!! (%d,%d)\n", pxy->x, pxy->y ); 473 | } 474 | } 475 | }; 476 | 477 | struct OnClickbck : public EventCallBack 478 | { 479 | NanoUiTest * parent; 480 | OnClickbck( NanoUiTest * parent ){ this->parent = parent; } 481 | virtual void operator()(UiEvent * p ) 482 | { 483 | } 484 | }; 485 | 486 | int initWidgets() 487 | { 488 | shared_ptr p(new Panel( "panel1","Panel Test", 10,10, 200,480 )); 489 | 490 | // preper callback function 491 | shared_ptr fp(new OnClickbck(this)); 492 | 493 | shared_ptr