├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── cmake └── modules │ ├── FindBZip2.cmake │ ├── FindBrotliDec.cmake │ ├── FindFreetype.cmake │ ├── FindHarfBuzz.cmake │ ├── FindPNG.cmake │ └── FindZLIB.cmake ├── data └── pg5827.txt ├── docs └── layout.md ├── examples ├── app.h ├── bitcode.cc ├── fontdb.cc ├── fontscan.cc ├── ftkitty.cc ├── ftrender.cc ├── genatlas.cc ├── glbinpack.cc ├── glcanvas.cc ├── gldemo.cc ├── glemoji.cc ├── glfont.cc ├── glglyph.cc ├── glgraph.cc ├── gllayout.cc ├── glsimple.cc ├── hashmap.h ├── ui9.h └── uniscan.cc ├── fonts ├── DejaVuSans-Bold.ttf ├── DejaVuSans-BoldOblique.ttf ├── DejaVuSans-ExtraLight.ttf ├── DejaVuSans-Oblique.ttf ├── DejaVuSans.ttf ├── DejaVuSansCondensed-Bold.ttf ├── DejaVuSansCondensed-BoldOblique.ttf ├── DejaVuSansCondensed-Oblique.ttf ├── DejaVuSansCondensed.ttf ├── DejaVuSansMono-Bold.ttf ├── DejaVuSansMono-BoldOblique.ttf ├── DejaVuSansMono-Oblique.ttf ├── DejaVuSansMono.ttf ├── DejaVuSerif-Bold.ttf ├── DejaVuSerif-BoldItalic.ttf ├── DejaVuSerif-Italic.ttf ├── DejaVuSerif.ttf ├── DejaVuSerifCondensed-Bold.ttf ├── DejaVuSerifCondensed-BoldItalic.ttf ├── DejaVuSerifCondensed-Italic.ttf ├── DejaVuSerifCondensed.ttf ├── LICENSE-DejaVu.txt ├── LICENSE-NotoColorEmoji.txt ├── LICENSE-Roboto.txt ├── NotoColorEmoji.ttf ├── Roboto-Black.ttf ├── Roboto-BlackItalic.ttf ├── Roboto-Bold.ttf ├── Roboto-BoldItalic.ttf ├── Roboto-Italic.ttf ├── Roboto-Light.ttf ├── Roboto-LightItalic.ttf ├── Roboto-Medium.ttf ├── Roboto-MediumItalic.ttf ├── Roboto-Regular.ttf ├── Roboto-Thin.ttf ├── Roboto-ThinItalic.ttf ├── RobotoMono-Bold.ttf ├── RobotoMono-BoldItalic.ttf ├── RobotoMono-Italic.ttf └── RobotoMono-Regular.ttf ├── images ├── glcanvas-1.png ├── glcanvas-2.png ├── glemoji.png ├── glfont.png ├── glglyph.png ├── glgraph.png ├── gllayout.png ├── glyb.png └── model.png ├── scripts ├── binpack-video-gif.sh ├── binpack-video-mp4.sh ├── glyphic-atlas-png.sh ├── glyphic-video-mp4.sh └── xcolortab-gen.py ├── shaders ├── canvas.fsh ├── default.properties ├── msdf.fsh ├── simple.fsh └── simple.vsh ├── src ├── binpack.cc ├── binpack.h ├── bitcode.h ├── canvas.cc ├── canvas.h ├── color.h ├── draw.h ├── file.cc ├── file.h ├── font.cc ├── font.h ├── format.h ├── geometry.h ├── glyph.cc ├── glyph.h ├── image.cc ├── image.h ├── logger.cc ├── logger.h ├── msdf.cc ├── msdf.h ├── multi.cc ├── multi.h ├── text.cc ├── text.h ├── utf8.cc ├── utf8.h ├── worker.h ├── ztdbits.h └── ztdendian.h └── tests ├── test0001.cc ├── test0002.cc ├── test0003.cc ├── test0004.cc ├── test0005.cc ├── test0006.cc ├── test0007.cc ├── test0008.cc ├── test0009.cc ├── test0010.cc ├── test0011.cc ├── test0012.cc └── test0013.cc /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | gmon.out 3 | fonts/*.csv 4 | fonts/*.png 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/harfbuzz"] 2 | path = third_party/harfbuzz 3 | url = https://github.com/harfbuzz/harfbuzz 4 | [submodule "third_party/freetype2"] 5 | path = third_party/freetype2 6 | url = https://github.com/freetype/freetype.git 7 | [submodule "third_party/glfw"] 8 | path = third_party/glfw 9 | url = https://github.com/glfw/glfw 10 | [submodule "third_party/glad"] 11 | path = third_party/glad 12 | url = https://github.com/Dav1dde/glad.git 13 | [submodule "third_party/msdfgen"] 14 | path = third_party/msdfgen 15 | url = https://github.com/Chlumsky/msdfgen.git 16 | [submodule "third_party/libpng"] 17 | path = third_party/libpng 18 | url = https://github.com/glennrp/libpng.git 19 | [submodule "third_party/zlib"] 20 | path = third_party/zlib 21 | url = https://github.com/madler/zlib.git 22 | [submodule "third_party/glm"] 23 | path = third_party/glm 24 | url = https://github.com/g-truc/glm.git 25 | [submodule "third_party/imgui"] 26 | path = third_party/imgui 27 | url = https://github.com/ocornut/imgui.git 28 | [submodule "third_party/bzip2"] 29 | path = third_party/bzip2 30 | url = https://github.com/libarchive/bzip2.git 31 | [submodule "third_party/brotli"] 32 | path = third_party/brotli 33 | url = https://github.com/google/brotli.git 34 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(glyb) 4 | 5 | # 6 | # project settings 7 | # 8 | 9 | set(CMAKE_CXX_STANDARD 17) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 12 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) 13 | 14 | # 15 | # compiler flags 16 | # 17 | 18 | include(CheckCXXCompilerFlag) 19 | 20 | if(MSVC) 21 | add_definitions(/MP) 22 | add_definitions(/D_SECURE_SCL=0 /D_WINDOWS /D_CRT_SECURE_NO_WARNINGS) 23 | endif() 24 | 25 | check_cxx_compiler_flag("-pg" has_gprof "int main() { return 0; }") 26 | if (CMAKE_PROFILE AND has_gprof) 27 | add_compile_options(-pg) 28 | endif() 29 | 30 | check_cxx_compiler_flag("-fno-omit-frame-pointer" has_no_omit_fp "int main() { return 0; }") 31 | if (has_no_omit_fp) 32 | add_compile_options(-fno-omit-frame-pointer) 33 | endif() 34 | 35 | # 36 | # user options 37 | # 38 | 39 | option(GLYB_BUILD_TOOLS "Build glyb tools" ON) 40 | option(GLYB_BUILD_EXAMPLES "Build glyb examples" ON) 41 | option(GLYB_BUILD_TESTS "Build glyb tests" ON) 42 | 43 | # 44 | # system libraries 45 | # 46 | 47 | find_package(Threads REQUIRED) 48 | list(APPEND GLYB_LIBS Threads::Threads) 49 | 50 | # 51 | # bundled libraries 52 | # 53 | 54 | ## glad 55 | add_subdirectory(third_party/glad) 56 | include_directories(${CMAKE_BINARY_DIR}/third_party/glad/include) 57 | 58 | ## glfw 59 | add_subdirectory(third_party/glfw) 60 | include_directories(third_party/glfw/include) 61 | 62 | # brotli 63 | set(BROTLI_BUNDLED_MODE OFF CACHE BOOL "") 64 | add_subdirectory(third_party/brotli) 65 | include_directories(third_party/brotli/c/include) 66 | set_property(TARGET brotlidec-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES) 67 | set_property(TARGET brotlicommon-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES) 68 | install(TARGETS brotlidec-static brotlicommon-static EXPORT brotli-targets DESTINATION "${INSTALL_BIN_DIR}") 69 | install(EXPORT brotli-targets DESTINATION lib) 70 | 71 | ## bzip2 72 | set(ENABLE_LIB_ONLY ON CACHE BOOL "") 73 | set(ENABLE_SHARED_LIB OFF CACHE BOOL "") 74 | set(ENABLE_STATIC_LIB ON CACHE BOOL "") 75 | add_subdirectory(third_party/bzip2) 76 | include_directories(third_party/bzip2) 77 | 78 | ## zlib 79 | add_subdirectory(third_party/zlib) 80 | include_directories(third_party/zlib) 81 | include_directories(${CMAKE_BINARY_DIR}/third_party/zlib) 82 | install(TARGETS zlib zlibstatic EXPORT zlib-targets DESTINATION "${INSTALL_BIN_DIR}") 83 | install(EXPORT zlib-targets DESTINATION lib) 84 | 85 | ## png 86 | add_subdirectory(third_party/libpng) 87 | include_directories(third_party/libpng) 88 | include_directories(${CMAKE_BINARY_DIR}/third_party/libpng) 89 | 90 | ## freetype2 91 | add_subdirectory(third_party/freetype2) 92 | add_definitions( -DFT_CONFIG_CONFIG_H=\"ftconfig.h\" ) 93 | add_definitions( -DFT_CONFIG_MODULES_H=\"ftmodule.h\" ) 94 | include_directories(third_party/freetype2/include) 95 | include_directories(third_party/freetype2/include/freetype/config) 96 | 97 | ## harfbuzz 98 | set(HB_HAVE_FREETYPE ON CACHE BOOL "Enable FreeType Integration" FORCE) 99 | add_subdirectory(third_party/harfbuzz) 100 | include_directories(third_party/harfbuzz/src) 101 | 102 | ## imgui 103 | set(IMGUI_SOURCES 104 | third_party/imgui/backends/imgui_impl_glfw.cpp 105 | third_party/imgui/backends/imgui_impl_opengl3.cpp 106 | third_party/imgui/imgui.cpp 107 | third_party/imgui/imgui_demo.cpp 108 | third_party/imgui/imgui_draw.cpp 109 | third_party/imgui/imgui_tables.cpp 110 | third_party/imgui/imgui_widgets.cpp) 111 | include_directories(third_party/imgui) 112 | include_directories(third_party/imgui/backends) 113 | add_definitions(-DIMGUI_IMPL_OPENGL_LOADER_GLAD) 114 | add_library(imgui STATIC ${IMGUI_SOURCES}) 115 | add_dependencies(imgui glad-generate-files) 116 | 117 | ## msdfgen 118 | add_subdirectory(third_party/msdfgen) 119 | include_directories(third_party/msdfgen) 120 | 121 | ## glm 122 | include_directories(third_party/glm) 123 | 124 | # 125 | # libglyb 126 | # 127 | 128 | include_directories(src) 129 | include_directories(examples) 130 | file(GLOB GLYB_SOURCES src/*.cc src/*.h) 131 | add_library(glyb STATIC ${GLYB_SOURCES}) 132 | add_dependencies(glyb glad-generate-files) 133 | set_target_properties(glyb PROPERTIES CXX_STANDARD 17) 134 | list(APPEND GLYB_LIBS 135 | ${PNG_LIBRARY} 136 | ${BROTLIDEC_LIBRARY} 137 | ${BZIP_LIBRARY} 138 | ${ZLIB_LIBRARY} 139 | ${HARFBUZZ_LIBRARY} 140 | ${FREETYPE_LIBRARY} 141 | glyb 142 | lib_msdfgen) 143 | 144 | # 145 | # CLI programs 146 | # 147 | 148 | if(GLYB_BUILD_TOOLS) 149 | foreach(prog IN ITEMS bitcode ftkitty ftrender fontdb fontscan genatlas uniscan) 150 | add_executable(${prog} examples/${prog}.cc) 151 | target_link_libraries(${prog} ${GLYB_LIBS} ${CMAKE_DL_LIBS}) 152 | endforeach(prog) 153 | endif() 154 | 155 | # 156 | # OpenGL examples 157 | # 158 | 159 | if(GLYB_BUILD_EXAMPLES) 160 | foreach(prog IN ITEMS glbinpack glemoji glgraph glglyph glfont gllayout glsimple gldemo) 161 | add_executable(${prog} examples/${prog}.cc) 162 | target_link_libraries(${prog} ${GLYB_LIBS} ${CMAKE_DL_LIBS} ${GLAD_LIBRARIES} glfw) 163 | target_compile_definitions(${prog} PRIVATE -DHAVE_GLAD) 164 | if(WIN32) 165 | set_property(TARGET ${prog} PROPERTY WIN32_EXECUTABLE TRUE) 166 | endif() 167 | endforeach(prog) 168 | 169 | add_executable(glcanvas examples/glcanvas.cc) 170 | target_link_libraries(glcanvas ${GLYB_LIBS} ${CMAKE_DL_LIBS} ${GLAD_LIBRARIES} glfw imgui) 171 | target_compile_definitions(glcanvas PRIVATE -DHAVE_GLAD) 172 | if(WIN32) 173 | set_property(TARGET glcanvas PROPERTY WIN32_EXECUTABLE TRUE) 174 | endif() 175 | endif() 176 | 177 | # 178 | # glyb tests 179 | # 180 | 181 | if(GLYB_BUILD_TESTS) 182 | file(GLOB BINPACK_TESTS tests/*.cc) 183 | foreach(src ${BINPACK_TESTS}) 184 | get_filename_component(name ${src} NAME_WE) 185 | add_executable(${name} ${src}) 186 | target_link_libraries(${name} ${GLYB_LIBS}) 187 | endforeach(src) 188 | endif() 189 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2019, Michael Clark 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /cmake/modules/FindBZip2.cmake: -------------------------------------------------------------------------------- 1 | set(BZip2_FOUND ON CACHE BOOL "" FORCE) 2 | set(BZZIP2_FOUND ON CACHE BOOL "" FORCE) 3 | set(BZIP2_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/third_party/bzip2 CACHE STRING "" FORCE) 4 | set(BZIP2_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/third_party/bzip2 CACHE STRING "" FORCE) 5 | set(BZIP2_LIBRARY bz2_static CACHE STRING "" FORCE) 6 | set(BZIP2_LIBRARIES bz2_static CACHE STRING "" FORCE) 7 | -------------------------------------------------------------------------------- /cmake/modules/FindBrotliDec.cmake: -------------------------------------------------------------------------------- 1 | set(BrotliDec_FOUND ON CACHE BOOL "" FORCE) 2 | set(BROTLIDEC_FOUND ON CACHE BOOL "" FORCE) 3 | set(BROTLIDEC_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/third_party/brotli/c CACHE STRING "" FORCE) 4 | set(BROTLIDEC_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/third_party/brotli/c CACHE STRING "" FORCE) 5 | set(BROTLIDEC_LIBRARY brotlidec-static CACHE STRING "" FORCE) 6 | set(BROTLIDEC_LIBRARIES brotlidec-static CACHE STRING "" FORCE) 7 | -------------------------------------------------------------------------------- /cmake/modules/FindFreetype.cmake: -------------------------------------------------------------------------------- 1 | set(Freetype_FOUND ON CACHE BOOL "" FORCE) 2 | set(FREETYPE_FOUND ON CACHE BOOL "" FORCE) 3 | set(FREETYPE_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/third_party/freetype2/include CACHE STRING "" FORCE) 4 | set(FREETYPE_INCLUDE_DIR_ft2build ${CMAKE_SOURCE_DIR}/third_party/freetype2/include CACHE STRING "" FORCE) 5 | set(FREETYPE_INCLUDE_DIR_freetype2 ${CMAKE_SOURCE_DIR}/third_party/freetype2/include CACHE STRING "" FORCE) 6 | set(FREETYPE_LIBRARY freetype CACHE STRING "" FORCE) 7 | set(FREETYPE_LIBRARIES freetype CACHE STRING "" FORCE) 8 | -------------------------------------------------------------------------------- /cmake/modules/FindHarfBuzz.cmake: -------------------------------------------------------------------------------- 1 | set(HarfBuzz_FOUND ON CACHE BOOL "" FORCE) 2 | set(HARFBUZZ_FOUND ON CACHE BOOL "" FORCE) 3 | set(HARFBUZZ_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/third_party/harfbuzz/src CACHE STRING "" FORCE) 4 | set(HARFBUZZ_LIBRARIES harfbuzz CACHE STRING "" FORCE) 5 | set(HARFBUZZ_LIBRARY harfbuzz CACHE STRING "" FORCE) 6 | -------------------------------------------------------------------------------- /cmake/modules/FindPNG.cmake: -------------------------------------------------------------------------------- 1 | set(PNG_FOUND ON CACHE BOOL "" FORCE) 2 | set(PNG_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/third_party/libpng CACHE STRING "" FORCE) 3 | set(PNG_LIBRARIES png_static CACHE STRING "" FORCE) 4 | set(PNG_LIBRARY png_static CACHE STRING "" FORCE) 5 | -------------------------------------------------------------------------------- /cmake/modules/FindZLIB.cmake: -------------------------------------------------------------------------------- 1 | set(ZLIB_FOUND ON CACHE BOOL "" FORCE) 2 | set(ZLIB_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/third_party/zlib CACHE STRING "" FORCE) 3 | set(ZLIB_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/third_party/zlib CACHE STRING "" FORCE) 4 | set(ZLIB_LIBRARIES zlibstatic CACHE STRING "" FORCE) 5 | set(ZLIB_LIBRARY zlibstatic CACHE STRING "" FORCE) 6 | -------------------------------------------------------------------------------- /docs/layout.md: -------------------------------------------------------------------------------- 1 | # Text Layout 2 | 3 | Placeholder for text layout documentation. 4 | 5 | ## Text Attributes 6 | 7 | The following attributes are supported by `text_layout`. 8 | 9 | - `font-family` 10 | - string e.g. Helvetica, Arial, Times, Times New Roman, etc. 11 | - `font-style` 12 | - string e.g. Regular, Bold, Bold Italic, Italic, etc. 13 | - `font-weight` 14 | - 100 = thin 15 | - 200 = extra light 16 | - 200 = light 17 | - 300 = light 18 | - 350 = semi light 19 | - 350 = book 20 | - 400 = normal 21 | - 400 = regular 22 | - 500 = medium 23 | - 600 = demibold 24 | - 600 = semibold 25 | - 700 = bold 26 | - 800 = extra bold 27 | - 800 = ultra bold 28 | - 900 = black 29 | - 900 = heavy 30 | - 950 = extra black 31 | - 950 = ultra black 32 | - `font-slope` 33 | - 0 = none 34 | - 1 = oblique 35 | - 1 = italic 36 | - `font-stretch` 37 | - 1 = ultra_condensed, 38 | - 2 = extra_condensed, 39 | - 3 = condensed, 40 | - 4 = semi_condensed, 41 | - 5 = medium, 42 | - 6 = semi_expanded, 43 | - 7 = expanded, 44 | - 8 = extra_expanded, 45 | - 9 = ultra_expanded, 46 | - `font-spacing` 47 | - 0 = normal, 48 | - 1 = monospaced, 49 | - `font-size` 50 | - integer points 51 | - `baseline-shift` 52 | - integer pixels, y-shift, positive value raises 53 | - `tracking` 54 | - integer pixels, y-spacing, added to character advance 55 | - `line-height` 56 | - integer pixels, y-height, added for new lines 57 | - `color` 58 | - HTML RGBA little-endian e.g. #ff000000 is black 59 | - `language` 60 | - defaults to `en`, passed to HarfBuzz 61 | -------------------------------------------------------------------------------- /examples/bitcode.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * order0 entropy coding 3 | * 4 | * - compress and decompress commands with stats 5 | * 6 | * Entropy Coding, Sachin Garg, 2006. 7 | * 8 | * Range coder based upon the carry-less implementation, 9 | * derived from work by Dmitry Subbotin. 10 | */ 11 | 12 | #undef NDEBUG 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include "bitcode.h" 29 | 30 | /* 31 | * file io 32 | */ 33 | 34 | /* 35 | * read file into std::vector using buffered file IO 36 | */ 37 | static size_t read_file(std::vector &buf, const char* filename) 38 | { 39 | FILE *f; 40 | struct stat statbuf; 41 | if ((f = fopen(filename, "r")) == nullptr) { 42 | fprintf(stderr, "fopen: %s\n", strerror(errno)); 43 | exit(1); 44 | } 45 | if (fstat(fileno(f), &statbuf) < 0) { 46 | fprintf(stderr, "fstat: %s\n", strerror(errno)); 47 | exit(1); 48 | } 49 | buf.resize(statbuf.st_size); 50 | size_t len = fread(buf.data(), 1, buf.size(), f); 51 | assert(buf.size() == len); 52 | fclose(f); 53 | return buf.size(); 54 | } 55 | 56 | /* 57 | * write file from std::vector using buffered file IO 58 | */ 59 | static size_t write_file(std::vector &buf, const char* filename) 60 | { 61 | FILE *f; 62 | if ((f = fopen(filename, "w")) == nullptr) { 63 | fprintf(stderr, "fopen: %s\n", strerror(errno)); 64 | exit(1); 65 | } 66 | size_t len = fwrite(buf.data(), 1, buf.size(), f); 67 | assert(buf.size() == len); 68 | fclose(f); 69 | return buf.size(); 70 | } 71 | 72 | /* 73 | * order0 range encode using adaptive frequencies 74 | */ 75 | template 76 | static size_t order0_encode(bitcode_reader &in, bitcode_writer &out, 77 | size_t input_size, FreqMode freq) 78 | { 79 | Coder c(&out); 80 | FreqTable t(256); 81 | 82 | for (size_t i = 0; i < input_size; i++) 83 | { 84 | uint8_t sym = in.read_fixed(8); 85 | c.EncodeRange(!sym ? 0 : t.CumFreq[sym-1], t.CumFreq[sym], t.CumFreq.back()); 86 | t.Update(freq, sym, c.MaxRange, i); 87 | } 88 | 89 | c.Flush(); 90 | out.flush(); 91 | 92 | return out.tell(); 93 | } 94 | 95 | /* 96 | * order0 range decode using adaptive frequencies 97 | */ 98 | template 99 | static size_t order0_decode(bitcode_reader &in, bitcode_writer &out, 100 | size_t output_size, FreqMode freq) 101 | { 102 | Coder c(&in); 103 | FreqTable t(256); 104 | 105 | c.Prime(); 106 | 107 | for (size_t i = 0; i < output_size; i++) 108 | { 109 | size_t Count = c.GetCurrentCount(t.CumFreq.back()); 110 | 111 | auto si = std::upper_bound(t.CumFreq.begin(), t.CumFreq.end(), Count); 112 | size_t sym = std::distance(t.CumFreq.begin(), si); 113 | 114 | out.write_fixed(sym, 8); 115 | c.RemoveRange(!sym ? 0 : t.CumFreq[sym-1], t.CumFreq[sym], t.CumFreq.back()); 116 | t.Update(freq, sym, c.MaxRange, i); 117 | } 118 | 119 | out.flush(); 120 | 121 | return out.tell(); 122 | } 123 | 124 | /* 125 | * order0 entropy coding compress and decompress commands with stats 126 | */ 127 | 128 | static clock_t start, end; 129 | static size_t loops = 1; 130 | static size_t input_size, output_size, decode_size; 131 | 132 | static double time_secs(clock_t start, clock_t end, size_t loops) 133 | { 134 | return (double) (end - start) / CLOCKS_PER_SEC / (double)loops; 135 | } 136 | 137 | static void print_results(const char* op, clock_t start, clock_t end, 138 | size_t input_size, size_t output_size, size_t TimingSize) 139 | { 140 | printf("%s: %zu -> %zu in %6.2f secs (%8.2f ns/byte)\n", 141 | op, input_size, output_size, time_secs(start, end, loops), 142 | time_secs(start, end, loops) / (double)TimingSize * 1e9); 143 | } 144 | 145 | static void do_compress(const char* in_filename, const char* out_filename, 146 | FreqMode freq) 147 | { 148 | vector_reader in; 149 | vector_writer out; 150 | bitcode_reader bin(&in); 151 | bitcode_writer bout(&out); 152 | 153 | input_size = read_file(in.buffer, in_filename); 154 | for (size_t i = 0; i < sizeof(size_t); i++) { 155 | out.buffer.push_back(((uint8_t*)&input_size)[i]); 156 | } 157 | out.buffer.resize(input_size + input_size/2); 158 | 159 | start = clock(); 160 | for (size_t l = 0; l < loops; l++) { 161 | bin.seek(0); 162 | bout.seek(sizeof(size_t)); 163 | output_size = order0_encode(bin, bout, input_size, freq); 164 | } 165 | end = clock(); 166 | 167 | out.buffer.resize(output_size += sizeof(size_t)); 168 | write_file(out.buffer, out_filename); 169 | print_results("Encode0", start, end, input_size, output_size, input_size); 170 | } 171 | 172 | static void do_decompress(const char* in_filename, const char* out_filename, 173 | FreqMode freq) 174 | { 175 | vector_reader in; 176 | vector_writer out; 177 | bitcode_reader bin(&in); 178 | bitcode_writer bout(&out); 179 | 180 | input_size = read_file(in.buffer, in_filename); 181 | for (size_t i = 0; i < sizeof(size_t); i++) { 182 | ((uint8_t *)&output_size)[i] = bin.read_fixed(8); 183 | } 184 | out.buffer.resize(output_size); 185 | 186 | start = clock(); 187 | for (size_t l = 0; l < loops; l++) { 188 | bin.seek(sizeof(size_t)); 189 | bout.seek(0); 190 | decode_size = order0_decode(bin, bout, output_size, freq); 191 | assert(output_size == decode_size); 192 | } 193 | end = clock(); 194 | 195 | write_file(out.buffer, out_filename); 196 | print_results("Decode0", start, end, input_size, output_size, output_size); 197 | } 198 | 199 | int main(int argc,char *argv[]) 200 | { 201 | loops = (argc == 6 ? atoi(argv[5]) : loops); 202 | if (argc < 5) { 203 | fprintf(stderr, "Usage: c|d s|p input_dataName output_dataName [loops]\n" 204 | "c: compress\n" 205 | "d: decompress\n" 206 | "s: freq_dyn_sym\n" 207 | "i: freq_dyn_interval\n"); 208 | exit(9); 209 | } else if (argv[1][0] == 'c' && argv[2][0] == 's') { 210 | do_compress(argv[3], argv[4], FreqModeDynPerSymbol); 211 | } else if (argv[1][0] == 'c' && argv[2][0] == 'i') { 212 | do_compress(argv[3], argv[4], FreqModeDynPerInterval); 213 | } else if (argv[1][0] == 'd' && argv[2][0] == 's') { 214 | do_decompress(argv[3], argv[4], FreqModeDynPerSymbol); 215 | } else if (argv[1][0] == 'd' && argv[2][0] == 'i') { 216 | do_decompress(argv[3], argv[4], FreqModeDynPerInterval); 217 | } else { 218 | fprintf(stderr, "%s: '%s' unknown command", argv[0], argv[1]); 219 | exit(9); 220 | } 221 | return 0; 222 | } 223 | -------------------------------------------------------------------------------- /examples/fontdb.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "binpack.h" 21 | #include "image.h" 22 | #include "draw.h" 23 | #include "font.h" 24 | #include "glyph.h" 25 | 26 | #ifdef _WIN32 27 | #define PATH_SEPARATOR "\\" 28 | #else 29 | #define PATH_SEPARATOR "/" 30 | #endif 31 | 32 | static font_manager_ft manager; 33 | static font_atlas atlas; 34 | 35 | static const char* font_dir = "fonts"; 36 | static bool print_list = false; 37 | static bool help_text = false; 38 | static int font_weight = -1; 39 | static int font_slope = -1; 40 | static int font_stretch = -1; 41 | static const char* font_name = "*"; 42 | 43 | void print_help(int argc, char **argv) 44 | { 45 | fprintf(stderr, 46 | "Usage: %s [options]\n" 47 | "\n" 48 | "Options:\n" 49 | " -n, --font-name font name\n" 50 | " -w, --font-weight font weight\n" 51 | " -s, --font-slant font slant\n" 52 | " -S, --font-stretch font stretch\n" 53 | " -l, --list list fonts\n" 54 | " -h, --help command line help\n", 55 | argv[0]); 56 | } 57 | 58 | 59 | bool check_param(bool cond, const char *param) 60 | { 61 | if (cond) { 62 | printf("error: %s requires parameter\n", param); 63 | } 64 | return (help_text = cond); 65 | } 66 | 67 | bool match_opt(const char *arg, const char *opt, const char *longopt) 68 | { 69 | return strcmp(arg, opt) == 0 || strcmp(arg, longopt) == 0; 70 | } 71 | 72 | void parse_options(int argc, char **argv) 73 | { 74 | int i = 1; 75 | while (i < argc) { 76 | if (match_opt(argv[i], "-d","--font-dir")) { 77 | if (check_param(++i == argc, "--font-dir")) break; 78 | font_dir = argv[i++]; 79 | } 80 | else if (match_opt(argv[i], "-n","--font-name")) { 81 | if (check_param(++i == argc, "--font-name")) break; 82 | font_name = argv[i++]; 83 | } 84 | else if (match_opt(argv[i], "-w", "--font-weight")) { 85 | if (check_param(++i == argc, "--font-weight")) break; 86 | font_weight = atoi(argv[i++]); 87 | } 88 | else if (match_opt(argv[i], "-s", "--font-slant")) { 89 | if (check_param(++i == argc, "--font-slant")) break; 90 | font_slope = atoi(argv[i++]); 91 | } 92 | else if (match_opt(argv[i], "-S", "--font-stretch")) { 93 | if (check_param(++i == argc, "--font-stretch")) break; 94 | font_stretch = atoi(argv[i++]); 95 | } else if (match_opt(argv[i], "-l", "--list")) { 96 | print_list = true; 97 | i++; 98 | } else if (match_opt(argv[i], "-h", "--help")) { 99 | help_text = true; 100 | i++; 101 | } else { 102 | fprintf(stderr, "error: unknown option: %s\n", argv[i]); 103 | help_text = true; 104 | break; 105 | } 106 | } 107 | 108 | if (help_text) { 109 | print_help(argc, argv); 110 | exit(1); 111 | } 112 | } 113 | 114 | int main(int argc, char **argv) 115 | { 116 | parse_options(argc, argv); 117 | 118 | manager.scanFontDir(font_dir); 119 | 120 | if (print_list) { 121 | for (auto &font : manager.getFontList()) { 122 | printf("%s\n", font->getFontData().toString().c_str()); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /examples/glemoji.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * glfw3 simple font render demo 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "binpack.h" 24 | #include "image.h" 25 | #include "draw.h" 26 | #include "font.h" 27 | #include "glyph.h" 28 | #include "logger.h" 29 | #include "app.h" 30 | 31 | using mat4 = glm::mat4; 32 | 33 | 34 | /* globals */ 35 | 36 | static program simple; 37 | static GLuint vao, vbo, ibo; 38 | static draw_list batch; 39 | static std::map tex_map; 40 | 41 | static mat4 mvp; 42 | static GLFWwindow* window; 43 | 44 | static int font_size = 270; 45 | static int window_width = 2560, window_height = 1440; 46 | static int framebuffer_width, framebuffer_height; 47 | static font_manager_ft manager; 48 | 49 | 50 | /* display */ 51 | 52 | static void display() 53 | { 54 | glClearColor(0.5f, 0.5f, 0.5f, 1.0f); 55 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 56 | 57 | for (auto img : batch.images) { 58 | auto ti = tex_map.find(img.iid); 59 | if (ti == tex_map.end()) { 60 | tex_map[img.iid] = image_create_texture(img); 61 | } else { 62 | image_update_texture(tex_map[img.iid], img); 63 | } 64 | } 65 | glBindVertexArray(vao); 66 | for (auto cmd : batch.cmds) { 67 | glUseProgram(simple.pid); 68 | glBindTexture(GL_TEXTURE_2D, tex_map[cmd.iid]); 69 | glDrawElements(cmd_mode_gl(cmd.mode), cmd.count, GL_UNSIGNED_INT, 70 | (void*)(cmd.offset * sizeof(uint))); 71 | } 72 | } 73 | 74 | static void reshape() 75 | { 76 | glfwGetWindowSize(window, &window_width, &window_height); 77 | glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height); 78 | 79 | mvp = glm::ortho(0.0f, (float)window_width, (float)window_height, 80 | 0.0f, 0.0f, 100.0f); 81 | 82 | glViewport(0, 0, framebuffer_width, framebuffer_height); 83 | 84 | uniform_matrix_4fv(&simple, "u_mvp", (const GLfloat *)&mvp[0][0]); 85 | } 86 | 87 | /* geometry */ 88 | 89 | static void update_geometry() 90 | { 91 | auto face = manager.findFontByPath("fonts/NotoColorEmoji.ttf"); 92 | 93 | const float x = 425.0f, y = 860.0f; 94 | const uint32_t color = 0xffffffff; 95 | 96 | std::vector shapes; 97 | text_shaper_ft shaper; 98 | text_renderer_ft renderer(&manager); 99 | //text_segment segment("📑📄📃📁📂📦", "en", face, font_size << 6, x, y, color); 100 | //text_segment segment("🐁🐇🐈🐎🐄", "en", face, font_size << 6, x, y, color); 101 | text_segment segment("🙃😙😃😜😍", "en", face, font_size << 6, x, y, color); 102 | 103 | draw_list_clear(batch); 104 | shaper.shape(shapes, segment); 105 | renderer.render(batch, shapes, segment); 106 | } 107 | 108 | static void vertex_array_config(program *prog) 109 | { 110 | vertex_array_pointer(prog, "a_pos", 3, GL_FLOAT, 0, &draw_vertex::pos); 111 | vertex_array_pointer(prog, "a_uv0", 2, GL_FLOAT, 0, &draw_vertex::uv); 112 | vertex_array_pointer(prog, "a_color", 4, GL_UNSIGNED_BYTE, 1, &draw_vertex::color); 113 | vertex_array_1f(prog, "a_gamma", 2.0f); 114 | } 115 | 116 | static void update_buffers() 117 | { 118 | /* create vertex and index arrays */ 119 | update_geometry(); 120 | glGenVertexArrays(1, &vao); 121 | glBindVertexArray(vao); 122 | vertex_buffer_create("vbo", &vbo, GL_ARRAY_BUFFER, batch.vertices); 123 | vertex_buffer_create("ibo", &ibo, GL_ELEMENT_ARRAY_BUFFER, batch.indices); 124 | vertex_array_config(&simple); 125 | glBindVertexArray(0); 126 | } 127 | 128 | /* OpenGL initialization */ 129 | 130 | static void initialize() 131 | { 132 | GLuint fsh, vsh; 133 | 134 | /* shader program */ 135 | vsh = compile_shader(GL_VERTEX_SHADER, "shaders/simple.vsh"); 136 | fsh = compile_shader(GL_FRAGMENT_SHADER, "shaders/simple.fsh"); 137 | link_program(&simple, vsh, fsh); 138 | glDeleteShader(vsh); 139 | glDeleteShader(fsh); 140 | 141 | /* create vertex buffers and font atlas texture */ 142 | update_buffers(); 143 | 144 | /* uniforms */ 145 | glUseProgram(simple.pid); 146 | uniform_1i(&simple, "u_tex0", 0); 147 | 148 | /* pipeline */ 149 | glEnable(GL_CULL_FACE); 150 | glFrontFace(GL_CCW); 151 | glCullFace(GL_BACK); 152 | glEnable(GL_BLEND); 153 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 154 | glDisable(GL_DEPTH_TEST); 155 | } 156 | 157 | /* GLFW GUI entry point */ 158 | 159 | static void refresh(GLFWwindow* window) 160 | { 161 | display(); 162 | glfwSwapBuffers(window); 163 | } 164 | 165 | static void resize(GLFWwindow* window, int, int) 166 | { 167 | reshape(); 168 | display(); 169 | glfwSwapBuffers(window); 170 | } 171 | 172 | static void glfont(int argc, char **argv) 173 | { 174 | glfwInit(); 175 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, CTX_OPENGL_MAJOR); 176 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, CTX_OPENGL_MINOR); 177 | 178 | window = glfwCreateWindow(window_width, window_height, argv[0], NULL, NULL); 179 | glfwMakeContextCurrent(window); 180 | gladLoadGL(); 181 | glfwSwapInterval(1); 182 | glfwSetFramebufferSizeCallback(window, resize); 183 | glfwSetWindowRefreshCallback(window, refresh); 184 | glfwGetWindowSize(window, &window_width, &window_height); 185 | glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height); 186 | 187 | initialize(); 188 | reshape(); 189 | while (!glfwWindowShouldClose(window)) { 190 | display(); 191 | glfwSwapBuffers(window); 192 | glfwPollEvents(); 193 | } 194 | 195 | glfwDestroyWindow(window); 196 | glfwTerminate(); 197 | } 198 | 199 | /* entry point */ 200 | 201 | static int app_main(int argc, char **argv) 202 | { 203 | manager.color_enabled = true; 204 | if (argc == 2) font_size = atoi(argv[1]); 205 | glfont(argc, argv); 206 | return 0; 207 | } 208 | 209 | declare_main(app_main) 210 | -------------------------------------------------------------------------------- /examples/gllayout.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * glfw3 simple font render demo 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "binpack.h" 24 | #include "image.h" 25 | #include "draw.h" 26 | #include "font.h" 27 | #include "glyph.h" 28 | #include "text.h" 29 | #include "logger.h" 30 | #include "app.h" 31 | 32 | using mat4 = glm::mat4; 33 | 34 | 35 | /* globals */ 36 | 37 | static GLuint vao, vbo, ibo; 38 | static program simple, msdf; 39 | static draw_list batch; 40 | static std::map tex_map; 41 | 42 | static mat4 mvp; 43 | static GLFWwindow* window; 44 | 45 | static int window_width = 2560, window_height = 1440; 46 | static int framebuffer_width, framebuffer_height; 47 | static font_manager_ft manager; 48 | static bool help_text = false; 49 | 50 | 51 | /* display */ 52 | 53 | static void update_uniforms(program *prog) 54 | { 55 | uniform_matrix_4fv(prog, "u_mvp", (const GLfloat *)&mvp[0][0]); 56 | uniform_1i(prog, "u_tex0", 0); 57 | } 58 | 59 | static program* cmd_shader_gl(int cmd_shader) 60 | { 61 | switch (cmd_shader) { 62 | case shader_simple: return &simple; 63 | case shader_msdf: return &msdf; 64 | default: return nullptr; 65 | } 66 | } 67 | 68 | static void display() 69 | { 70 | glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 71 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 72 | 73 | for (auto img : batch.images) { 74 | auto ti = tex_map.find(img.iid); 75 | if (ti == tex_map.end()) { 76 | tex_map[img.iid] = image_create_texture(img); 77 | } 78 | } 79 | glBindVertexArray(vao); 80 | for (auto cmd : batch.cmds) { 81 | glUseProgram(cmd_shader_gl(cmd.shader)->pid); 82 | glBindTexture(GL_TEXTURE_2D, tex_map[cmd.iid]); 83 | glDrawElements(cmd_mode_gl(cmd.mode), cmd.count, GL_UNSIGNED_INT, 84 | (void*)(cmd.offset * sizeof(uint))); 85 | } 86 | } 87 | 88 | static void reshape() 89 | { 90 | glfwGetWindowSize(window, &window_width, &window_height); 91 | glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height); 92 | 93 | mvp = glm::ortho(0.0f, (float)window_width, (float)window_height, 94 | 0.0f, 0.0f, 100.0f); 95 | 96 | glViewport(0, 0, framebuffer_width, framebuffer_height); 97 | 98 | glUseProgram(msdf.pid); 99 | update_uniforms(&msdf); 100 | 101 | glUseProgram(simple.pid); 102 | update_uniforms(&simple); 103 | } 104 | 105 | /* geometry */ 106 | 107 | static void update_geometry() 108 | { 109 | std::vector segments; 110 | std::vector shapes; 111 | 112 | text_shaper_hb shaper; 113 | text_renderer_ft renderer(&manager); 114 | text_layout layout(&manager, &shaper, &renderer); 115 | text_container c; 116 | 117 | c.append(text_span("Γειά ", 118 | {{ "font-family", "roboto" }, { "font-style", "regular" }, { "font-size", "24" }, { "tracking", "2" }, { "baseline-shift", "9" }, { "color", "#800000" }})); 119 | c.append(text_span("σου ", 120 | {{ "font-family", "roboto" }, { "font-style", "regular" }, { "font-size", "24" }, { "tracking", "2" }, { "baseline-shift", "6" }, { "color", "#008000" }})); 121 | c.append(text_span("Κόσμε ", 122 | {{ "font-family", "roboto" }, { "font-style", "regular" }, { "font-size", "24" }, { "tracking", "2" }, { "baseline-shift", "3" }, { "color", "#000080" }})); 123 | c.append(text_span( 124 | " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor " 125 | "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud " 126 | "exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " 127 | "dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " 128 | "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " 129 | "mollit anim id est laborum. ", 130 | {{ "font-family", "roboto mono" }, { "font-style", "regular" }, { "font-size", "36" }, { "color", "#000040" }})); 131 | c.append(text_span( 132 | " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor " 133 | "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud " 134 | "exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " 135 | "dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " 136 | "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " 137 | "mollit anim id est laborum. ", 138 | {{ "font-family", "roboto" }, { "font-style", "light italic" }, { "font-size", "72" }, { "color", "#7f7f9f" }})); 139 | c.append(text_span( 140 | " Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor " 141 | "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud " 142 | "exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " 143 | "dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " 144 | "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " 145 | "mollit anim id est laborum. ", 146 | {{ "font-family", "roboto" }, { "font-style", "bold" }, { "font-size", "72" }, { "color", "#7f7f9f" }})); 147 | 148 | draw_list_clear(batch); 149 | layout.layout(segments, c, 50, 50, 2400, 700); 150 | for (auto &segment : segments) { 151 | shapes.clear(); 152 | shaper.shape(shapes, segment); 153 | renderer.render(batch, shapes, segment); 154 | } 155 | } 156 | 157 | static void vertex_array_config(program *prog) 158 | { 159 | vertex_array_pointer(prog, "a_pos", 3, GL_FLOAT, 0, &draw_vertex::pos); 160 | vertex_array_pointer(prog, "a_uv0", 2, GL_FLOAT, 0, &draw_vertex::uv); 161 | vertex_array_pointer(prog, "a_color", 4, GL_UNSIGNED_BYTE, 1, &draw_vertex::color); 162 | vertex_array_1f(prog, "a_gamma", 2.0f); 163 | } 164 | 165 | static void update_buffers() 166 | { 167 | /* create vertex and index arrays */ 168 | update_geometry(); 169 | glGenVertexArrays(1, &vao); 170 | glBindVertexArray(vao); 171 | vertex_buffer_create("vbo", &vbo, GL_ARRAY_BUFFER, batch.vertices); 172 | vertex_buffer_create("ibo", &ibo, GL_ELEMENT_ARRAY_BUFFER, batch.indices); 173 | vertex_array_config(&simple); 174 | glBindVertexArray(0); 175 | } 176 | 177 | /* OpenGL initialization */ 178 | 179 | static void initialize() 180 | { 181 | GLuint simple_fsh, msdf_fsh, vsh; 182 | 183 | /* shader program */ 184 | vsh = compile_shader(GL_VERTEX_SHADER, "shaders/simple.vsh"); 185 | simple_fsh = compile_shader(GL_FRAGMENT_SHADER, "shaders/simple.fsh"); 186 | msdf_fsh = compile_shader(GL_FRAGMENT_SHADER, "shaders/msdf.fsh"); 187 | link_program(&simple, vsh, simple_fsh); 188 | link_program(&msdf, vsh, msdf_fsh); 189 | glDeleteShader(vsh); 190 | glDeleteShader(simple_fsh); 191 | glDeleteShader(msdf_fsh); 192 | 193 | /* load font metadata */ 194 | manager.scanFontDir("fonts"); 195 | 196 | /* create vertex buffers and font atlas texture */ 197 | update_buffers(); 198 | 199 | /* pipeline */ 200 | glEnable(GL_CULL_FACE); 201 | glFrontFace(GL_CCW); 202 | glCullFace(GL_BACK); 203 | glEnable(GL_BLEND); 204 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 205 | glDisable(GL_DEPTH_TEST); 206 | glLineWidth(1.0); 207 | } 208 | 209 | /* GLFW GUI entry point */ 210 | 211 | static void refresh(GLFWwindow* window) 212 | { 213 | display(); 214 | glfwSwapBuffers(window); 215 | } 216 | 217 | static void resize(GLFWwindow* window, int, int) 218 | { 219 | reshape(); 220 | display(); 221 | glfwSwapBuffers(window); 222 | } 223 | 224 | static void glfont(int argc, char **argv) 225 | { 226 | glfwInit(); 227 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, CTX_OPENGL_MAJOR); 228 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, CTX_OPENGL_MINOR); 229 | 230 | window = glfwCreateWindow(window_width, window_height, argv[0], NULL, NULL); 231 | glfwMakeContextCurrent(window); 232 | gladLoadGL(); 233 | glfwSwapInterval(1); 234 | glfwSetFramebufferSizeCallback(window, resize); 235 | glfwSetWindowRefreshCallback(window, refresh); 236 | glfwGetWindowSize(window, &window_width, &window_height); 237 | glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height); 238 | 239 | initialize(); 240 | reshape(); 241 | while (!glfwWindowShouldClose(window)) { 242 | display(); 243 | glfwSwapBuffers(window); 244 | glfwPollEvents(); 245 | } 246 | 247 | glfwDestroyWindow(window); 248 | glfwTerminate(); 249 | } 250 | 251 | /* help text */ 252 | 253 | static void print_help(int argc, char **argv) 254 | { 255 | fprintf(stderr, 256 | "Usage: %s [options]\n" 257 | " -h, --help command line help\n" 258 | " -y, --overlay-stats show statistics overlay\n" 259 | " -m, --enable-msdf enable MSDF font rendering\n" 260 | " -M, --disable-autoload disable MSDF atlas autoloading\n", 261 | argv[0]); 262 | } 263 | 264 | /* option parsing */ 265 | 266 | static bool check_param(bool cond, const char *param) 267 | { 268 | if (cond) { 269 | printf("error: %s requires parameter\n", param); 270 | } 271 | return (help_text = cond); 272 | } 273 | 274 | static bool match_opt(const char *arg, const char *opt, const char *longopt) 275 | { 276 | return strcmp(arg, opt) == 0 || strcmp(arg, longopt) == 0; 277 | } 278 | 279 | static void parse_options(int argc, char **argv) 280 | { 281 | int i = 1; 282 | while (i < argc) { 283 | if (match_opt(argv[i], "-h", "--help")) { 284 | help_text = true; 285 | i++; 286 | } else if (match_opt(argv[i], "-d", "--debug")) { 287 | logger::set_level(logger::L::Ldebug); 288 | i++; 289 | } else if (match_opt(argv[i], "-m", "--enable-msdf")) { 290 | manager.msdf_enabled = true; 291 | i++; 292 | } else if (match_opt(argv[i], "-M", "--disable-autoload")) { 293 | manager.msdf_autoload = false; 294 | i++; 295 | } else { 296 | fprintf(stderr, "error: unknown option: %s\n", argv[i]); 297 | help_text = true; 298 | break; 299 | } 300 | } 301 | 302 | if (help_text) { 303 | print_help(argc, argv); 304 | exit(1); 305 | } 306 | 307 | } 308 | 309 | /* entry point */ 310 | 311 | static int app_main(int argc, char **argv) 312 | { 313 | parse_options(argc, argv); 314 | glfont(argc, argv); 315 | return 0; 316 | } 317 | 318 | declare_main(app_main) 319 | -------------------------------------------------------------------------------- /examples/glsimple.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * glfw3 simple font render demo 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "binpack.h" 24 | #include "image.h" 25 | #include "draw.h" 26 | #include "font.h" 27 | #include "glyph.h" 28 | #include "logger.h" 29 | #include "app.h" 30 | 31 | using mat4 = glm::mat4; 32 | 33 | 34 | /* globals */ 35 | 36 | static program simple; 37 | static GLuint vao, vbo, ibo; 38 | static draw_list batch; 39 | static std::map tex_map; 40 | 41 | static mat4 mvp; 42 | static GLFWwindow* window; 43 | 44 | static const char *font_path = "fonts/RobotoMono-Regular.ttf"; 45 | static const char *render_text = "the quick brown fox jumps over the lazy dog"; 46 | static const char* text_lang = "en"; 47 | static int font_size = 32; 48 | static int width = 1024, height = 256; 49 | static font_manager_ft manager; 50 | 51 | 52 | /* display */ 53 | 54 | static void display() 55 | { 56 | glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 57 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 58 | 59 | for (auto img : batch.images) { 60 | auto ti = tex_map.find(img.iid); 61 | if (ti == tex_map.end()) { 62 | tex_map[img.iid] = image_create_texture(img); 63 | } 64 | } 65 | glBindVertexArray(vao); 66 | for (auto cmd : batch.cmds) { 67 | glUseProgram(simple.pid); 68 | glBindTexture(GL_TEXTURE_2D, tex_map[cmd.iid]); 69 | glDrawElements(cmd_mode_gl(cmd.mode), cmd.count, GL_UNSIGNED_INT, 70 | (void*)(cmd.offset * sizeof(uint))); 71 | } 72 | } 73 | 74 | static void reshape(int width, int height) 75 | { 76 | mvp = glm::ortho(0.0f, (float)width,(float)height, 0.0f, 0.0f, 100.0f); 77 | uniform_matrix_4fv(&simple, "u_mvp", (const GLfloat *)&mvp[0][0]); 78 | glViewport(0, 0, width, height); 79 | } 80 | 81 | /* geometry */ 82 | 83 | static void update_geometry() 84 | { 85 | auto face = manager.findFontByPath(font_path); 86 | 87 | const float x = 100.0f, y = 100.0f + (float)font_size; 88 | const uint32_t color = 0xff000000; 89 | 90 | std::vector shapes; 91 | text_shaper_hb shaper; 92 | text_renderer_ft renderer(&manager); 93 | text_segment segment(render_text, text_lang, face, 94 | font_size * 64, x, y, color); 95 | 96 | draw_list_clear(batch); 97 | shaper.shape(shapes, segment); 98 | renderer.render(batch, shapes, segment); 99 | } 100 | 101 | static void vertex_array_config(program *prog) 102 | { 103 | vertex_array_pointer(prog, "a_pos", 3, GL_FLOAT, 0, &draw_vertex::pos); 104 | vertex_array_pointer(prog, "a_uv0", 2, GL_FLOAT, 0, &draw_vertex::uv); 105 | vertex_array_pointer(prog, "a_color", 4, GL_UNSIGNED_BYTE, 1, &draw_vertex::color); 106 | vertex_array_1f(prog, "a_gamma", 2.0f); 107 | } 108 | 109 | static void update_buffers() 110 | { 111 | /* create vertex and index arrays */ 112 | update_geometry(); 113 | glGenVertexArrays(1, &vao); 114 | glBindVertexArray(vao); 115 | vertex_buffer_create("vbo", &vbo, GL_ARRAY_BUFFER, batch.vertices); 116 | vertex_buffer_create("ibo", &ibo, GL_ELEMENT_ARRAY_BUFFER, batch.indices); 117 | vertex_array_config(&simple); 118 | glBindVertexArray(0); 119 | } 120 | 121 | /* OpenGL initialization */ 122 | 123 | static void initialize() 124 | { 125 | GLuint fsh, vsh; 126 | 127 | /* shader program */ 128 | vsh = compile_shader(GL_VERTEX_SHADER, "shaders/simple.vsh"); 129 | fsh = compile_shader(GL_FRAGMENT_SHADER, "shaders/simple.fsh"); 130 | link_program(&simple, vsh, fsh); 131 | glDeleteShader(vsh); 132 | glDeleteShader(fsh); 133 | 134 | /* create vertex buffers and font atlas texture */ 135 | update_buffers(); 136 | 137 | /* uniforms */ 138 | glUseProgram(simple.pid); 139 | uniform_1i(&simple, "u_tex0", 0); 140 | 141 | /* pipeline */ 142 | glEnable(GL_CULL_FACE); 143 | glFrontFace(GL_CCW); 144 | glCullFace(GL_BACK); 145 | glEnable(GL_BLEND); 146 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 147 | glDisable(GL_DEPTH_TEST); 148 | } 149 | 150 | /* GLFW GUI entry point */ 151 | 152 | static void refresh(GLFWwindow* window) 153 | { 154 | display(); 155 | glfwSwapBuffers(window); 156 | } 157 | 158 | static void resize(GLFWwindow* window, int width, int height) 159 | { 160 | reshape(width, height); 161 | display(); 162 | glfwSwapBuffers(window); 163 | } 164 | 165 | static void glfont(int argc, char **argv) 166 | { 167 | glfwInit(); 168 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, CTX_OPENGL_MAJOR); 169 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, CTX_OPENGL_MINOR); 170 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 171 | 172 | window = glfwCreateWindow(width, height, argv[0], NULL, NULL); 173 | glfwMakeContextCurrent(window); 174 | gladLoadGL(); 175 | glfwSwapInterval(1); 176 | glfwSetFramebufferSizeCallback(window, resize); 177 | glfwSetWindowRefreshCallback(window, refresh); 178 | glfwGetFramebufferSize(window, &width, &height); 179 | 180 | initialize(); 181 | reshape(width, height); 182 | while (!glfwWindowShouldClose(window)) { 183 | display(); 184 | glfwSwapBuffers(window); 185 | glfwPollEvents(); 186 | } 187 | 188 | glfwDestroyWindow(window); 189 | glfwTerminate(); 190 | } 191 | 192 | /* entry point */ 193 | 194 | static int app_main(int argc, char **argv) 195 | { 196 | glfont(argc, argv); 197 | return 0; 198 | } 199 | 200 | declare_main(app_main) 201 | -------------------------------------------------------------------------------- /fonts/DejaVuSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSans-Bold.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSans-BoldOblique.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSans-BoldOblique.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSans-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSans-ExtraLight.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSans-Oblique.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSans-Oblique.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSans.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSansCondensed-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSansCondensed-Bold.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSansCondensed-BoldOblique.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSansCondensed-BoldOblique.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSansCondensed-Oblique.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSansCondensed-Oblique.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSansCondensed.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSansCondensed.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSansMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSansMono-Bold.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSansMono-BoldOblique.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSansMono-BoldOblique.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSansMono-Oblique.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSansMono-Oblique.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSansMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSansMono.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSerif-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSerif-Bold.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSerif-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSerif-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSerif-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSerif-Italic.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSerif.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSerif.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSerifCondensed-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSerifCondensed-Bold.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSerifCondensed-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSerifCondensed-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSerifCondensed-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSerifCondensed-Italic.ttf -------------------------------------------------------------------------------- /fonts/DejaVuSerifCondensed.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/DejaVuSerifCondensed.ttf -------------------------------------------------------------------------------- /fonts/LICENSE-DejaVu.txt: -------------------------------------------------------------------------------- 1 | Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. 2 | Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) 3 | 4 | 5 | Bitstream Vera Fonts Copyright 6 | ------------------------------ 7 | 8 | Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is 9 | a trademark of Bitstream, Inc. 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of the fonts accompanying this license ("Fonts") and associated 13 | documentation files (the "Font Software"), to reproduce and distribute the 14 | Font Software, including without limitation the rights to use, copy, merge, 15 | publish, distribute, and/or sell copies of the Font Software, and to permit 16 | persons to whom the Font Software is furnished to do so, subject to the 17 | following conditions: 18 | 19 | The above copyright and trademark notices and this permission notice shall 20 | be included in all copies of one or more of the Font Software typefaces. 21 | 22 | The Font Software may be modified, altered, or added to, and in particular 23 | the designs of glyphs or characters in the Fonts may be modified and 24 | additional glyphs or characters may be added to the Fonts, only if the fonts 25 | are renamed to names not containing either the words "Bitstream" or the word 26 | "Vera". 27 | 28 | This License becomes null and void to the extent applicable to Fonts or Font 29 | Software that has been modified and is distributed under the "Bitstream 30 | Vera" names. 31 | 32 | The Font Software may be sold as part of a larger software package but no 33 | copy of one or more of the Font Software typefaces may be sold by itself. 34 | 35 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 36 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, 37 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, 38 | TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME 39 | FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING 40 | ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, 41 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 42 | THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE 43 | FONT SOFTWARE. 44 | 45 | Except as contained in this notice, the names of Gnome, the Gnome 46 | Foundation, and Bitstream Inc., shall not be used in advertising or 47 | otherwise to promote the sale, use or other dealings in this Font Software 48 | without prior written authorization from the Gnome Foundation or Bitstream 49 | Inc., respectively. For further information, contact: fonts at gnome dot 50 | org. 51 | 52 | Arev Fonts Copyright 53 | ------------------------------ 54 | 55 | Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. 56 | 57 | Permission is hereby granted, free of charge, to any person obtaining 58 | a copy of the fonts accompanying this license ("Fonts") and 59 | associated documentation files (the "Font Software"), to reproduce 60 | and distribute the modifications to the Bitstream Vera Font Software, 61 | including without limitation the rights to use, copy, merge, publish, 62 | distribute, and/or sell copies of the Font Software, and to permit 63 | persons to whom the Font Software is furnished to do so, subject to 64 | the following conditions: 65 | 66 | The above copyright and trademark notices and this permission notice 67 | shall be included in all copies of one or more of the Font Software 68 | typefaces. 69 | 70 | The Font Software may be modified, altered, or added to, and in 71 | particular the designs of glyphs or characters in the Fonts may be 72 | modified and additional glyphs or characters may be added to the 73 | Fonts, only if the fonts are renamed to names not containing either 74 | the words "Tavmjong Bah" or the word "Arev". 75 | 76 | This License becomes null and void to the extent applicable to Fonts 77 | or Font Software that has been modified and is distributed under the 78 | "Tavmjong Bah Arev" names. 79 | 80 | The Font Software may be sold as part of a larger software package but 81 | no copy of one or more of the Font Software typefaces may be sold by 82 | itself. 83 | 84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL 88 | TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 92 | OTHER DEALINGS IN THE FONT SOFTWARE. 93 | 94 | Except as contained in this notice, the name of Tavmjong Bah shall not 95 | be used in advertising or otherwise to promote the sale, use or other 96 | dealings in this Font Software without prior written authorization 97 | from Tavmjong Bah. For further information, contact: tavmjong @ free 98 | . fr. 99 | 100 | TeX Gyre DJV Math 101 | ----------------- 102 | Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. 103 | 104 | Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski 105 | (on behalf of TeX users groups) are in public domain. 106 | 107 | Letters imported from Euler Fraktur from AMSfonts are (c) American 108 | Mathematical Society (see below). 109 | Bitstream Vera Fonts Copyright 110 | Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera 111 | is a trademark of Bitstream, Inc. 112 | 113 | Permission is hereby granted, free of charge, to any person obtaining a copy 114 | of the fonts accompanying this license (“Fonts”) and associated 115 | documentation 116 | files (the “Font Software”), to reproduce and distribute the Font Software, 117 | including without limitation the rights to use, copy, merge, publish, 118 | distribute, 119 | and/or sell copies of the Font Software, and to permit persons to whom 120 | the Font Software is furnished to do so, subject to the following 121 | conditions: 122 | 123 | The above copyright and trademark notices and this permission notice 124 | shall be 125 | included in all copies of one or more of the Font Software typefaces. 126 | 127 | The Font Software may be modified, altered, or added to, and in particular 128 | the designs of glyphs or characters in the Fonts may be modified and 129 | additional 130 | glyphs or characters may be added to the Fonts, only if the fonts are 131 | renamed 132 | to names not containing either the words “Bitstream” or the word “Vera”. 133 | 134 | This License becomes null and void to the extent applicable to Fonts or 135 | Font Software 136 | that has been modified and is distributed under the “Bitstream Vera” 137 | names. 138 | 139 | The Font Software may be sold as part of a larger software package but 140 | no copy 141 | of one or more of the Font Software typefaces may be sold by itself. 142 | 143 | THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 144 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, 145 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, 146 | TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME 147 | FOUNDATION 148 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, 149 | SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN 150 | ACTION 151 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR 152 | INABILITY TO USE 153 | THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. 154 | Except as contained in this notice, the names of GNOME, the GNOME 155 | Foundation, 156 | and Bitstream Inc., shall not be used in advertising or otherwise to promote 157 | the sale, use or other dealings in this Font Software without prior written 158 | authorization from the GNOME Foundation or Bitstream Inc., respectively. 159 | For further information, contact: fonts at gnome dot org. 160 | 161 | AMSFonts (v. 2.2) copyright 162 | 163 | The PostScript Type 1 implementation of the AMSFonts produced by and 164 | previously distributed by Blue Sky Research and Y&Y, Inc. are now freely 165 | available for general use. This has been accomplished through the 166 | cooperation 167 | of a consortium of scientific publishers with Blue Sky Research and Y&Y. 168 | Members of this consortium include: 169 | 170 | Elsevier Science IBM Corporation Society for Industrial and Applied 171 | Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS) 172 | 173 | In order to assure the authenticity of these fonts, copyright will be 174 | held by 175 | the American Mathematical Society. This is not meant to restrict in any way 176 | the legitimate use of the fonts, such as (but not limited to) electronic 177 | distribution of documents containing these fonts, inclusion of these fonts 178 | into other public domain or commercial font collections or computer 179 | applications, use of the outline data to create derivative fonts and/or 180 | faces, etc. However, the AMS does require that the AMS copyright notice be 181 | removed from any derivative versions of the fonts which have been altered in 182 | any way. In addition, to ensure the fidelity of TeX documents using Computer 183 | Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces, 184 | has requested that any alterations which yield different font metrics be 185 | given a different name. 186 | 187 | $Id$ 188 | -------------------------------------------------------------------------------- /fonts/LICENSE-NotoColorEmoji.txt: -------------------------------------------------------------------------------- 1 | This Font Software is licensed under the SIL Open Font License, 2 | Version 1.1. 3 | 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | ----------------------------------------------------------- 8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 9 | ----------------------------------------------------------- 10 | 11 | PREAMBLE 12 | The goals of the Open Font License (OFL) are to stimulate worldwide 13 | development of collaborative font projects, to support the font 14 | creation efforts of academic and linguistic communities, and to 15 | provide a free and open framework in which fonts may be shared and 16 | improved in partnership with others. 17 | 18 | The OFL allows the licensed fonts to be used, studied, modified and 19 | redistributed freely as long as they are not sold by themselves. The 20 | fonts, including any derivative works, can be bundled, embedded, 21 | redistributed and/or sold with any software provided that any reserved 22 | names are not used by derivative works. The fonts and derivatives, 23 | however, cannot be released under any other type of license. The 24 | requirement for fonts to remain under this license does not apply to 25 | any document created using the fonts or their derivatives. 26 | 27 | DEFINITIONS 28 | "Font Software" refers to the set of files released by the Copyright 29 | Holder(s) under this license and clearly marked as such. This may 30 | include source files, build scripts and documentation. 31 | 32 | "Reserved Font Name" refers to any names specified as such after the 33 | copyright statement(s). 34 | 35 | "Original Version" refers to the collection of Font Software 36 | components as distributed by the Copyright Holder(s). 37 | 38 | "Modified Version" refers to any derivative made by adding to, 39 | deleting, or substituting -- in part or in whole -- any of the 40 | components of the Original Version, by changing formats or by porting 41 | the Font Software to a new environment. 42 | 43 | "Author" refers to any designer, engineer, programmer, technical 44 | writer or other person who contributed to the Font Software. 45 | 46 | PERMISSION & CONDITIONS 47 | Permission is hereby granted, free of charge, to any person obtaining 48 | a copy of the Font Software, to use, study, copy, merge, embed, 49 | modify, redistribute, and sell modified and unmodified copies of the 50 | Font Software, subject to the following conditions: 51 | 52 | 1) Neither the Font Software nor any of its individual components, in 53 | Original or Modified Versions, may be sold by itself. 54 | 55 | 2) Original or Modified Versions of the Font Software may be bundled, 56 | redistributed and/or sold with any software, provided that each copy 57 | contains the above copyright notice and this license. These can be 58 | included either as stand-alone text files, human-readable headers or 59 | in the appropriate machine-readable metadata fields within text or 60 | binary files as long as those fields can be easily viewed by the user. 61 | 62 | 3) No Modified Version of the Font Software may use the Reserved Font 63 | Name(s) unless explicit written permission is granted by the 64 | corresponding Copyright Holder. This restriction only applies to the 65 | primary font name as presented to the users. 66 | 67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 68 | Software shall not be used to promote, endorse or advertise any 69 | Modified Version, except to acknowledge the contribution(s) of the 70 | Copyright Holder(s) and the Author(s) or with their explicit written 71 | permission. 72 | 73 | 5) The Font Software, modified or unmodified, in part or in whole, 74 | must be distributed entirely under this license, and must not be 75 | distributed under any other license. The requirement for fonts to 76 | remain under this license does not apply to any document created using 77 | the Font Software. 78 | 79 | TERMINATION 80 | This license becomes null and void if any of the above conditions are 81 | not met. 82 | 83 | DISCLAIMER 84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 92 | OTHER DEALINGS IN THE FONT SOFTWARE. 93 | -------------------------------------------------------------------------------- /fonts/NotoColorEmoji.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/NotoColorEmoji.ttf -------------------------------------------------------------------------------- /fonts/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/Roboto-Black.ttf -------------------------------------------------------------------------------- /fonts/Roboto-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/Roboto-BlackItalic.ttf -------------------------------------------------------------------------------- /fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /fonts/Roboto-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/Roboto-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/Roboto-Italic.ttf -------------------------------------------------------------------------------- /fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /fonts/Roboto-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/Roboto-LightItalic.ttf -------------------------------------------------------------------------------- /fonts/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/Roboto-Medium.ttf -------------------------------------------------------------------------------- /fonts/Roboto-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/Roboto-MediumItalic.ttf -------------------------------------------------------------------------------- /fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /fonts/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/Roboto-Thin.ttf -------------------------------------------------------------------------------- /fonts/Roboto-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/Roboto-ThinItalic.ttf -------------------------------------------------------------------------------- /fonts/RobotoMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/RobotoMono-Bold.ttf -------------------------------------------------------------------------------- /fonts/RobotoMono-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/RobotoMono-BoldItalic.ttf -------------------------------------------------------------------------------- /fonts/RobotoMono-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/RobotoMono-Italic.ttf -------------------------------------------------------------------------------- /fonts/RobotoMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/fonts/RobotoMono-Regular.ttf -------------------------------------------------------------------------------- /images/glcanvas-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/images/glcanvas-1.png -------------------------------------------------------------------------------- /images/glcanvas-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/images/glcanvas-2.png -------------------------------------------------------------------------------- /images/glemoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/images/glemoji.png -------------------------------------------------------------------------------- /images/glfont.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/images/glfont.png -------------------------------------------------------------------------------- /images/glglyph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/images/glglyph.png -------------------------------------------------------------------------------- /images/glgraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/images/glgraph.png -------------------------------------------------------------------------------- /images/gllayout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/images/gllayout.png -------------------------------------------------------------------------------- /images/glyb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/images/glyb.png -------------------------------------------------------------------------------- /images/model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaeljclark/glyb/74fc97e6ce610da7895431e8c26df5def65bf9d8/images/model.png -------------------------------------------------------------------------------- /scripts/binpack-video-gif.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | video=build/video 4 | 5 | test -d ${video}/ppm || mkdir -p ${video}/ppm 6 | test -d ${video}/gif || mkdir -p ${video}/gif 7 | 8 | rm -f ${video}/ppm/* ${video}/gif/binpack.gif 9 | 10 | ./build/glbinpack --offline \ 11 | --frame-size 1024x576 --frame-step 5 --frame-count 10000 \ 12 | --template "${video}/ppm/binpack-%09d.ppm" 13 | 14 | ffmpeg -framerate 25 -i "${video}/ppm/binpack-%09d.ppm" \ 15 | -s 1024x576 -f gif -loop 1000 \ 16 | ${video}/gif/binpack.gif -hide_banner 17 | -------------------------------------------------------------------------------- /scripts/binpack-video-mp4.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | video=build/video 4 | 5 | test -d ${video}/ppm || mkdir -p ${video}/ppm 6 | test -d ${video}/mp4 || mkdir -p ${video}/mp4 7 | 8 | rm -f ${video}/ppm/* ${video}/mp4/binpack.mp4 9 | 10 | ./build/glbinpack --offline \ 11 | --bin-size 1024x1024 --random 32,32 \ 12 | --frame-size 1920x1080 --frame-step 5 --frame-count 10000 \ 13 | --template "${video}/ppm/binpack-%09d.ppm" 14 | 15 | ffmpeg -framerate 25 -i "${video}/ppm/binpack-%09d.ppm" \ 16 | -s 1920x1080 -acodec aac -vf format=yuv420p \ 17 | -preset veryslow -profile:v baseline \ 18 | ${video}/mp4/binpack.mp4 -hide_banner 19 | -------------------------------------------------------------------------------- /scripts/glyphic-atlas-png.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | atlas=build/atlas 4 | 5 | test -d ${atlas}/png || mkdir -p ${atlas}/png 6 | 7 | rm -f ${atlas}/png/* 8 | 9 | ./build/glyphic --offline --quadruple \ 10 | --frame-skip 100 --frame-count 1 \ 11 | --template "/dev/null" \ 12 | --dump-atlases "${atlas}/png/glyphic-%09d-regular.png" 13 | 14 | ./build/glyphic --offline --quadruple --enable-msdf \ 15 | --frame-skip 100 --frame-count 1 \ 16 | --template "/dev/null" \ 17 | --dump-atlases "${atlas}/png/glyphic-%09d-msdf.png" 18 | -------------------------------------------------------------------------------- /scripts/glyphic-video-mp4.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | video=build/video 4 | 5 | test -d ${video}/ppm || mkdir -p ${video}/ppm 6 | test -d ${video}/mp4 || mkdir -p ${video}/mp4 7 | 8 | rm -f ${video}/ppm/* ${video}/mp4/glyphic-*.mp4 9 | 10 | ./build/glyphic --offline --quadruple \ 11 | --frame-size 1920x1080 --frame-rate 60 \ 12 | --frame-skip 900 --frame-count 900 \ 13 | --template "${video}/ppm/glyphic-%09d-regular.ppm" 14 | 15 | ffmpeg -framerate 25 -i "${video}/ppm/glyphic-%09d-regular.ppm" \ 16 | -s 1920x1080 -crf 0 \ 17 | -preset veryslow -threads 36 \ 18 | ${video}/mp4/glyphic-regular.mp4 -hide_banner 19 | 20 | ./build/glyphic --offline --quadruple --enable-msdf \ 21 | --frame-size 1920x1080 --frame-rate 60 \ 22 | --frame-skip 900 --frame-count 900 \ 23 | --template "${video}/ppm/glyphic-%09d-msdf.ppm" 24 | 25 | ffmpeg -framerate 25 -i "${video}/ppm/glyphic-%09d-msdf.ppm" \ 26 | -s 1920x1080 -crf 0 \ 27 | -preset veryslow -threads 36 \ 28 | ${video}/mp4/glyphic-msdf.mp4 -hide_banner 29 | -------------------------------------------------------------------------------- /scripts/xcolortab-gen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import sys 4 | 5 | def capitalize(s): 6 | return s[0:1].upper() + s[1:] 7 | def normalize(name): 8 | return ''.join(map(capitalize, name)) 9 | 10 | # read colors 11 | names = set() 12 | colors = [] 13 | with open("/etc/X11/rgb.txt", "r") as f: 14 | for l in f: 15 | r, g, b, *name = l.strip().split() 16 | s = normalize(name) 17 | if s not in names: 18 | names.add(s) 19 | try: 20 | v = int(r) | (int(g)<<8) | (int(b)<<16) | (255<<24) 21 | colors.append({ 'name': s, 'rgba': v }) 22 | except ValueError: 23 | pass 24 | 25 | # check command line arguments 26 | filter = len(sys.argv) > 1 and sys.argv[1] == "--filter" 27 | 28 | # print color array 29 | print("static struct xcolor xcolortab[] = {") 30 | for c in sorted(colors, key = lambda i: i['name']): 31 | x = sum(1 for c in c['name'] if c.isupper()) 32 | if filter and x > 1 or c['name'][-1].isdigit(): 33 | continue 34 | print(" { %-24s 0x%08x }," % ("\"%s\"," % c['name'], c['rgba'])) 35 | print(" { %-24s 0x%08x }," % ("0,", 0)) 36 | print("};") -------------------------------------------------------------------------------- /shaders/default.properties: -------------------------------------------------------------------------------- 1 | *.visible = true 2 | *.enabled = true 3 | *.can-focus = true 4 | *.can-move = false 5 | *.position = 0,0 6 | *.minimum-size = 0,0,0 7 | *.maximum-size = 1e9,1e9,1e9 8 | *.preferred-size = 0,0,0 9 | *.padding = 0,0,0,0 10 | *.border = 0,0,0,0 11 | *.margin = 0,0,0,0 12 | *.border-radius = 5 13 | *.fill-color = #000000 14 | *.stroke-color = #ffffff 15 | *.text-color = #ffffff 16 | *.text-leading = 1.5 17 | *.font-size = 15.0 18 | *.font-family = DejaVu Sans 19 | *.font-style = Book 20 | 21 | Frame.fill-color = #f0f0f0 22 | Frame.stroke-color = #404040 23 | Frame.text-color = #000000 24 | Frame.click-radius = 10 25 | Frame.border-radius = 5 26 | Frame.margin = 0 27 | Frame.border = 0.75 28 | Frame.padding = 5 29 | Frame.can-move = true 30 | 31 | Label.fill-color = #ffffff 32 | Label.stroke-color = #808080 33 | Label.text-color = #000000 34 | Label.border-radius = 0 35 | Label.margin = 0 36 | Label.border = 0.75 37 | Label.padding = 2 38 | 39 | Slider.fill-color = #ffffff 40 | Slider.stroke-color = #808080 41 | Slider.text-color = #000000 42 | Slider.border-radius = 5 43 | Slider.margin = 0 44 | Slider.border = 0.75 45 | Slider.padding = 5 46 | Slider.control-size = 11.5 47 | Slider.bar-thickness = 10 48 | 49 | Switch.fill-color = #ffffff 50 | Switch.active-color = #707070 51 | Switch.inactive-color = #303030 52 | Switch.stroke-color = #202020 53 | Switch.text-color = #000000 54 | Switch.margin = 0 55 | Switch.border = 0.75 56 | Switch.padding = 10 57 | Switch.border-radius = 15 58 | Switch.control-radius = 12.5 59 | Switch.control-size = 30,15 60 | -------------------------------------------------------------------------------- /shaders/msdf.fsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | in vec4 v_color; 4 | in vec2 v_uv0; 5 | in float v_gamma; 6 | 7 | uniform sampler2D u_tex0; 8 | 9 | out vec4 outFragColor; 10 | 11 | float median(float r, float g, float b) { 12 | return max(min(r, g), min(max(r, g), b)); 13 | } 14 | 15 | #define SQRT2_2 0.70710678118654757 /* 1 / sqrt(2.) */ 16 | 17 | void main() 18 | { 19 | vec3 sample = texture(u_tex0, v_uv0).rgb; 20 | 21 | ivec2 sz = textureSize( u_tex0, 0 ); 22 | //vec2 sz = vec2(1024,1024); 23 | 24 | float dx = dFdx( v_uv0.x ) * sz.x; 25 | float dy = dFdy( v_uv0.y ) * sz.y; 26 | float toPixels = 16.0 * inversesqrt( dx * dx + dy * dy ); 27 | float sigDist = median( sample.r, sample.g, sample.b ) - 0.5; 28 | float alpha = clamp( sigDist * toPixels + 0.5, 0.0, 1.0 ); 29 | 30 | outFragColor = vec4(pow(v_color.rgb, vec3(1.0/v_gamma)), alpha); 31 | } 32 | -------------------------------------------------------------------------------- /shaders/simple.fsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | in vec4 v_color; 4 | in vec2 v_uv0; 5 | in float v_gamma; 6 | 7 | uniform sampler2D u_tex0; 8 | 9 | out vec4 outFragColor; 10 | 11 | void main() { 12 | vec4 t_color = texture(u_tex0, v_uv0); 13 | outFragColor = v_color * vec4(pow(t_color.rgb, vec3(1.0/v_gamma)), t_color.a); 14 | } 15 | -------------------------------------------------------------------------------- /shaders/simple.vsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | in vec3 a_pos; 4 | in vec2 a_uv0; 5 | in vec4 a_color; 6 | in vec4 a_stroke; 7 | in float a_gamma; 8 | in float a_width; 9 | in float a_shape; 10 | 11 | uniform mat4 u_mvp; 12 | 13 | out vec4 v_color; 14 | out vec2 v_uv0; 15 | out float v_gamma; 16 | out float v_shape; 17 | 18 | void main() { 19 | v_color = a_color; 20 | v_gamma = a_gamma; 21 | v_uv0 = a_uv0; 22 | v_shape = a_shape; 23 | gl_Position = u_mvp * vec4(a_pos.xyz, 1.0); 24 | } 25 | -------------------------------------------------------------------------------- /src/binpack.h: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #pragma once 4 | 5 | /* 6 | * bin_packer 7 | * 8 | * 2D bin packer implementing the MAXRECTS-BSSF algorithm 9 | */ 10 | 11 | struct bin_point 12 | { 13 | int x, y; 14 | 15 | bin_point() : x(0), y(0) {} 16 | bin_point(int s) : x(s), y(s) {} 17 | bin_point(int x, int y) : x(x), y(y) {} 18 | bin_point(const bin_point &o) : x(o.x), y(o.y) {} 19 | 20 | bool operator==(const bin_point &o) { return x == o.x && y == o.y; } 21 | bool operator!=(const bin_point &o) { return !(*this == o); } 22 | 23 | bin_point& operator+=(const bin_point &o) { x += o.x; y += o.y; return *this; } 24 | bin_point& operator-=(const bin_point &o) { x -= o.x; y -= o.y; return *this; } 25 | 26 | bin_point operator+(const bin_point &o) const { return bin_point(x + o.x, y + o.y); } 27 | bin_point operator-(const bin_point &o) const { return bin_point(x - o.x, y - o.y); } 28 | 29 | bin_point operator+(const int i) const { return bin_point(x + i, y + i); } 30 | bin_point operator-(const int i) const { return bin_point(x - i, y - i); } 31 | 32 | bool operator==(const bin_point &o) const { return x == o.x && y == o.y; } 33 | bool operator<(const bin_point &o) const { return x < o.x || (x == o.x && y < o.y); } 34 | 35 | /* axiomatically define other comparisons in terms of "equals" and "less than" */ 36 | bool operator!=(const bin_point &o) const { return !(*this == o); } 37 | bool operator<=(const bin_point &o) const { return *this < o || *this == o; } 38 | bool operator>(const bin_point &o) const { return !(*this <= o); } 39 | bool operator>=(const bin_point &o) const { return !(*this < o) || *this == o; } 40 | }; 41 | 42 | struct bin_rect 43 | { 44 | bin_point a, b; 45 | 46 | inline bin_rect() : a(0), b(0) {} 47 | inline bin_rect(bin_point a, bin_point b) : a(a), b(b) { if (a > b) std::swap(a, b); } 48 | inline bin_rect(const bin_rect &o) : a(o.a), b(o.b) {} 49 | 50 | inline int width() const { return b.x - a.x; } 51 | inline int height() const { return b.y - a.y; } 52 | inline int area() const { return (b.x - a.x) * (b.y - a.y); } 53 | inline bin_point size() const { return bin_point(b.x - a.x, b.y - a.y); } 54 | 55 | inline bool operator==(const bin_rect &o) { return a == o.a && b == o.b; } 56 | inline bool operator!=(const bin_rect &o) { return !(*this == o); } 57 | 58 | inline bool contains(bin_rect o) 59 | { 60 | return ( a.x <= o.a.x && b.x >= o.b.x && a.y <= o.a.y && b.y >= o.b.y ); 61 | } 62 | 63 | inline bool intersects(bin_rect o) 64 | { 65 | return ( a.x < o.b.x && b.x > o.a.x && a.y < o.b.y && b.y > o.a.y ); 66 | } 67 | 68 | std::vector intersect_subset(bin_rect o); 69 | std::vector disjoint_subset(bin_rect o); 70 | }; 71 | 72 | struct bin_packer 73 | { 74 | bin_rect total; 75 | std::vector free_list; 76 | std::map alloc_map; 77 | size_t contained_min; 78 | 79 | bin_packer() = delete; 80 | bin_packer(bin_point sz); 81 | 82 | void reset(); 83 | void set_bin_size(bin_point sz); 84 | void split_intersecting_nodes(bin_rect b); 85 | void remove_containing_nodes(); 86 | std::pair scan_bins(bin_point sz); 87 | std::pair find_region(int idx, bin_point sz); 88 | void create_explicit(int idx, bin_rect rect); 89 | size_t verify(); 90 | void dump(); 91 | }; -------------------------------------------------------------------------------- /src/color.h: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #pragma once 4 | 5 | /* color */ 6 | 7 | class color 8 | { 9 | public: 10 | float r, g, b, a; 11 | 12 | color(); 13 | color(unsigned rgba); 14 | color(float r, float g, float b, float a); 15 | color(const color &o); 16 | color(std::string hex); 17 | 18 | std::string to_string() const; 19 | unsigned int rgba32() const; 20 | 21 | color saturate(float f) const; 22 | color brighten(float f) const; 23 | 24 | bool operator==(const color &o); 25 | bool operator!=(const color &o); 26 | }; 27 | 28 | inline color::color() : r(0), g(0), b(0), a(0) {} 29 | 30 | inline color::color(unsigned rgba) : 31 | r(((rgba >> 24) & 0x0ff) / 256.0f), 32 | g(((rgba >> 16) & 0xff) / 256.0f), 33 | b(((rgba >> 8) & 0xff) / 256.0f), 34 | a((rgba & 0xff) / 256.0f) {} 35 | 36 | inline color::color(float r, float g, float b, float a) : 37 | r(r), g(g), b(b), a(a) {} 38 | 39 | inline color::color(const color &o) : 40 | r(o.r), g(o.g), b(o.b), a(o.a) {} 41 | 42 | inline color::color(std::string hex) 43 | { 44 | if (!hex.length()) { 45 | r = g = b = a = 1.0f; 46 | return; 47 | } 48 | if (hex[0] == '#') { 49 | hex = hex.substr(1); 50 | } 51 | unsigned rgba = 0; 52 | sscanf(hex.c_str(), "%x", &rgba); 53 | if (hex.length() == 8) { 54 | r = ((rgba >> 24) & 0xff) / 255.0f; 55 | g = ((rgba >> 16) & 0xff) / 255.0f; 56 | b = ((rgba >> 8) & 0xff) / 255.0f; 57 | a = (rgba & 0xff) / 255.0f; 58 | } else if (hex.length() == 6) { 59 | r = ((rgba >> 16) & 0xff) / 255.0f; 60 | g = ((rgba >> 8) & 0xff) / 255.0f; 61 | b = (rgba & 0xff) / 255.0f; 62 | a = 1.0f; 63 | } else if (hex.length() == 2) { 64 | r = g = b = (rgba & 0xff) / 255.0f; 65 | a = 1.0f; 66 | } else { 67 | r = g = b = a = 1.0f; 68 | } 69 | } 70 | 71 | inline char hexdigit(int d) { return (d < 10) ? '0' + d : 'A' + (d-10); } 72 | 73 | inline std::string color::to_string() const 74 | { 75 | char buf[10]; 76 | union { unsigned rgba; unsigned char c[4]; } tou32 = { rgba32() }; 77 | sprintf(buf, "#%c%c%c%c%c%c%c%c", 78 | hexdigit(tou32.c[0] / 16), hexdigit(tou32.c[0] % 16), 79 | hexdigit(tou32.c[1] / 16), hexdigit(tou32.c[1] % 16), 80 | hexdigit(tou32.c[2] / 16), hexdigit(tou32.c[2] % 16), 81 | hexdigit(tou32.c[3] / 16), hexdigit(tou32.c[3] % 16)); 82 | return std::string(buf); 83 | } 84 | 85 | inline unsigned int color::rgba32() const 86 | { 87 | union { unsigned char c[4]; unsigned rgba; } tou32 = { 88 | (unsigned char)std::max(0, std::min(255, (int)(r * 255.0f))), 89 | (unsigned char)std::max(0, std::min(255, (int)(g * 255.0f))), 90 | (unsigned char)std::max(0, std::min(255, (int)(b * 255.0f))), 91 | (unsigned char)std::max(0, std::min(255, (int)(a * 255.0f))) 92 | }; 93 | return tou32.rgba; 94 | } 95 | 96 | inline color color::saturate(float f) const 97 | { 98 | float l = 0.299f * r + 0.587f * g + 0.114f * b; // CCIR601 perceived luminance 99 | return color(f * r + (1.0f-f) * l, f * g + (1.0f-f) * l, f * b + (1.0f-f) * l, a); 100 | } 101 | 102 | inline color color::brighten(float f) const 103 | { 104 | return color(r * f > 1.0f ? 1.0f : r * f, 105 | g * f > 1.0f ? 1.0f : g * f, 106 | b * f > 1.0f ? 1.0f : b * f, a); 107 | } 108 | 109 | inline bool color::operator==(const color &o) 110 | { 111 | return r == o.r && g == o.g && b == o.b && a == o.a; 112 | } 113 | 114 | inline bool color::operator!=(const color &o) 115 | { 116 | return !(*this == o); 117 | } 118 | -------------------------------------------------------------------------------- /src/draw.h: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #pragma once 4 | 5 | typedef unsigned uint; 6 | 7 | typedef struct { 8 | float pos[3]; 9 | float uv[2]; 10 | uint color; 11 | float shape; 12 | } draw_vertex; 13 | 14 | enum { tbo_iid = -1 }; 15 | 16 | enum { 17 | st_clamp = (1 << 1), 18 | st_wrap = (1 << 2), 19 | filter_nearest = (1 << 3), 20 | filter_linear = (1 << 4), 21 | }; 22 | 23 | typedef struct { 24 | int iid; 25 | int size[3]; 26 | int modrect[4]; 27 | int flags; 28 | uint8_t *pixels; 29 | } draw_image; 30 | 31 | enum { 32 | image_none = 0 33 | }; 34 | 35 | enum { 36 | mode_triangles = 1, 37 | mode_lines = 2, 38 | }; 39 | 40 | enum { 41 | shader_simple = 1, 42 | shader_msdf = 2, 43 | shader_canvas = 3, 44 | }; 45 | 46 | typedef struct { 47 | uint viewport[4]; 48 | uint iid; 49 | uint mode; 50 | uint shader; 51 | uint offset; 52 | uint count; 53 | } draw_cmd; 54 | 55 | typedef struct { 56 | std::vector images; 57 | std::vector cmds; 58 | std::vector vertices; 59 | std::vector indices; 60 | } draw_list; 61 | 62 | inline void draw_list_clear(draw_list &batch) 63 | { 64 | batch.images.clear(); 65 | batch.cmds.clear(); 66 | batch.vertices.clear(); 67 | batch.indices.clear(); 68 | } 69 | 70 | inline void draw_list_viewport(draw_list &batch, uint x, uint y, uint w, uint h) 71 | { 72 | bool empty = batch.cmds.size() == 0; 73 | draw_cmd &last = batch.cmds.back(); 74 | 75 | if (empty || 76 | (last.viewport[0] != x && 77 | last.viewport[1] != y && 78 | last.viewport[2] != w && 79 | last.viewport[3] != h)) 80 | { 81 | batch.cmds.push_back({{ x, y, w, h }, 82 | !empty ? last.iid : 0, 83 | !empty ? last.mode : 0, 84 | !empty ? last.shader : 0, 85 | !empty ? last.offset + last.count : 0, 86 | 0 87 | }); 88 | } 89 | } 90 | 91 | inline uint draw_list_vertex(draw_list &batch, draw_vertex v) 92 | { 93 | auto i = batch.vertices.insert(batch.vertices.end(), v); 94 | return (uint)(i - batch.vertices.begin()); 95 | } 96 | 97 | inline void draw_list_indices(draw_list &batch, uint iid, uint mode, uint shader, 98 | std::initializer_list l) 99 | { 100 | bool empty = batch.cmds.size() == 0; 101 | draw_cmd &last = batch.cmds.back(); 102 | 103 | uint start, end; 104 | 105 | start = (uint)batch.indices.size(); 106 | batch.indices.insert(batch.indices.end(), l.begin(), l.end()); 107 | end = (uint)batch.indices.size(); 108 | 109 | if (empty || 110 | last.iid != iid || 111 | last.mode != mode || 112 | last.shader != shader) 113 | { 114 | uint vp[4]; 115 | if (empty) { 116 | memset(vp, 0, sizeof(vp)); 117 | } else { 118 | memcpy(vp, last.viewport, sizeof(vp)); 119 | } 120 | batch.cmds.push_back({{ vp[0], vp[1], vp[2], vp[3] }, iid, mode, shader, start, end - start }); 121 | } else { 122 | last.count += (end - start); 123 | } 124 | } 125 | 126 | inline void draw_list_image_delta(draw_list &batch, image *img, bin_rect delta, int flags) 127 | { 128 | int w = img->getWidth(), h = img->getHeight(), d = img->getBytesPerPixel(); 129 | 130 | draw_image drim{img->iid, {w,h,d}, { delta.a.x, delta.a.y, 131 | (delta.b.x - delta.a.x), (delta.b.y - delta.a.y) 132 | }, flags, img->getData()}; 133 | 134 | auto i = std::lower_bound(batch.images.begin(), batch.images.end(), drim, 135 | [](const draw_image &l, const draw_image &r) { return l.iid < r.iid; }); 136 | 137 | if (i == batch.images.end() || i->iid != drim.iid) { 138 | i = batch.images.insert(i, drim); 139 | } else { 140 | if (delta.a.x == w && delta.a.y == h) { 141 | // do nothing 142 | } 143 | else if (i->modrect[0] == 0 && i->modrect[1] == 0 && 144 | i->modrect[2] == w && i->modrect[3] == h) { 145 | // set delta 146 | i->modrect[0] = delta.a.x; 147 | i->modrect[1] = delta.a.y; 148 | i->modrect[2] = (delta.b.x - delta.a.x); 149 | i->modrect[3] = (delta.b.y - delta.a.y); 150 | } 151 | else { 152 | // widen delta 153 | int x1 = std::min(i->modrect[0],delta.a.x); 154 | int y1 = std::min(i->modrect[1],delta.a.y); 155 | int x2 = std::max(i->modrect[0]+i->modrect[2],delta.b.x); 156 | int y2 = std::max(i->modrect[1]+i->modrect[3],delta.b.y); 157 | i->modrect[0] = x1; 158 | i->modrect[1] = y1; 159 | i->modrect[2] = x2-x1; 160 | i->modrect[3] = y2-y1; 161 | } 162 | } 163 | } 164 | 165 | inline void draw_list_image(draw_list &batch, image *img, int flags) 166 | { 167 | bin_rect delta(bin_point((int)img->getWidth(),(int)img->getHeight()),bin_point(0,0)); 168 | 169 | return draw_list_image_delta(batch, img, delta, flags); 170 | } 171 | -------------------------------------------------------------------------------- /src/file.h: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #pragma once 4 | 5 | #ifdef _WIN32 6 | typedef long long ssize_t; 7 | #ifndef PATH_MAX 8 | #define PATH_MAX MAX_PATH 9 | #endif 10 | #endif 11 | 12 | struct file; 13 | typedef std::shared_ptr file_ptr; 14 | 15 | struct file 16 | { 17 | std::string sbuf; 18 | size_t sbufOffset; 19 | 20 | std::string path; 21 | std::string errmsg; 22 | int errcode; 23 | 24 | file(std::string path); 25 | virtual ~file() = default; 26 | 27 | std::string getPath(); 28 | std::string getBasename(); 29 | std::string getErrorMessage(); 30 | int getErrorCode(); 31 | bool copyToPath(std::string destpath); 32 | char* readLine(char *buf, size_t buflen); 33 | 34 | virtual bool open(int mode) = 0; 35 | virtual ssize_t read(void *buf, size_t buflen) = 0; 36 | virtual const void* getBuffer() = 0; 37 | virtual ssize_t getLength() = 0; 38 | virtual int asFileDescriptor(ssize_t *outStart, ssize_t *outLength) = 0; 39 | virtual ssize_t seek(ssize_t offset, int whence) = 0; 40 | virtual void close(); 41 | 42 | static bool dirExists(std::string dname); 43 | static bool fileExists(std::string fname); 44 | static bool makeDir(std::string dname); 45 | 46 | static std::string dirName(std::string path); 47 | static std::string baseName(std::string path); 48 | 49 | static std::vector list(std::string dirname); 50 | static std::string getPath(std::string rsrc); 51 | static file_ptr getResource(std::string rsrc); 52 | static file_ptr getFile(std::string filename); 53 | static std::string getExecutablePath(); 54 | static std::string getExecutableDirectory(); 55 | static std::string getTempDir(); 56 | static std::string getHomeDir(); 57 | static std::string getTempFile(std::string filename, std::string suffix); 58 | }; 59 | 60 | inline file::file(std::string path) : 61 | sbuf(""), sbufOffset(0), path(path), errmsg("unknown"), errcode(0) {} -------------------------------------------------------------------------------- /src/format.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static std::string format(const char* fmt, ...) 9 | { 10 | std::vector buf; 11 | int len; 12 | va_list ap; 13 | 14 | va_start(ap, fmt); 15 | len = vsnprintf(nullptr, 0, fmt, ap); 16 | va_end(ap); 17 | 18 | buf.resize(len+1); 19 | va_start(ap, fmt); 20 | vsnprintf(buf.data(), len+1, fmt, ap); 21 | va_end(ap); 22 | 23 | return std::string(buf.data(), len); 24 | } 25 | -------------------------------------------------------------------------------- /src/geometry.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Rectangle Intersection (2D) 3 | * 4 | * Copyright (c) 2020 Michael Clark 5 | * 6 | * License: GPLv3 7 | * 8 | * Perform 2D rectangle intersection test and returns intersection 9 | * topology classes including which axes overlap and the proximal 10 | * direction of the rectangles relative to each other when they are 11 | * not overlapping. The interface for the intersect function is: 12 | * 13 | * - intersect_2d intersect(rect_2d r1, rect_2d r2); 14 | * 15 | * The rectangle structure contains two points {x, y} relative to 16 | * origin with 'x' increasing from left to right and 'y' increasing 17 | * fromn top to bottom. 18 | * 19 | * - struct rect_2d { vec2 p0, p1; }; 20 | * 21 | * The intersect_2d topology classes are coded using 5 bits: 22 | * 23 | * - { inner, north, south, east, west } 24 | * 25 | * The intersect function returns combinations of these bits forming 26 | * 24 distinct classes, with coding efficiency of 91.7% 27 | * 28 | * D = log₂(|classes|)/bits 29 | * = log₂(24)/5 30 | * = 4.585/5 31 | * = 0.917 32 | * 33 | * The algorithm performs 8 less than comparisons {r1,r2}·{p0,p1}·{x,y} 34 | * from which it decides the class. Insideness is tested with not less 35 | * than p0 and less than p1, so coordinates may need to be biased with 36 | * an appropriate epsilon value. 𝛆 = {0.5, 0.5}. 37 | */ 38 | 39 | #pragma once 40 | 41 | //* Rectangle (2D) *// 42 | struct rect_2d 43 | { 44 | glm::vec2 p0, p1; 45 | }; 46 | 47 | //* Intersection topology classes *// 48 | enum intersect_2d 49 | { 50 | /* 51 | * Outside cases (0-axis crossing) 52 | * 53 | * ░░░░░ ░░░░░ ░░░░░ █░░░░ ░░█░░ ░░░░█ ░░░░░ ░░░░░ 54 | * ░┏━┓░ ░┏━┓░ ░┏━┓░ ░┏━┓░ ░┏━┓░ ░┏━┓░ ░┏━┓░ ░┏━┓░ 55 | * ░┃░┃░ ░┃░┃░ █┃░┃░ ░┃░┃░ ░┃░┃░ ░┃░┃░ ░┃░┃█ ░┃░┃░ 56 | * ░┗━┛░ ░┗━┛░ ░┗━┛░ ░┗━┛░ ░┗━┛░ ░┗━┛░ ░┗━┛░ ░┗━┛░ 57 | * ░░█░░ █░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░█ 58 | * 59 | * Fully inside case (0-axis crossing): 60 | * 61 | * ░░░░░ 62 | * ░┏━┓░ 63 | * ░┃█┃░ 64 | * ░┗━┛░ 65 | * ░░░░░ 66 | * 67 | * Overlap cases (1-axis crossing): 68 | * 69 | * ░░░░░ ░░░░░ ░░█░░ ░░░░░ 70 | * ░┏━┓░ ░┏━┓░ ░┏█┓░ ░┏━┓░ 71 | * ░┃░┃░ ██░┃░ ░┃░┃░ ░┃░██ 72 | * ░┗█┛░ ░┗━┛░ ░┗━┛░ ░┗━┛░ 73 | * ░░█░░ ░░░░░ ░░░░░ ░░░░░ 74 | * 75 | * Overlap cases (2-axis crossing): 76 | * 77 | * ░░░░░ ██░░░ ░░░██ ░░░░░ ░░█░░ ░░░░░ 78 | * ░┏━┓░ ██━┓░ ░┏━██ ░┏━┓░ ░┏█┓░ ░┏━┓░ 79 | * ░┃░┃░ ░┃░┃░ ░┃░┃░ ░┃░┃░ ░┃█┃░ █████ 80 | * ██━┛░ ░┗━┛░ ░┗━┛░ ░┗━██ ░┗█┛░ ░┗━┛░ 81 | * ██░░░ ░░░░░ ░░░░░ ░░░██ ░░█░░ ░░░░░ 82 | * 83 | * Overlap cases (3-axis crossing): 84 | * 85 | * ██░░░ █████ ░░░██ ░░░░░ 86 | * ██━┓░ █████ ░┏━██ ░┏━┓░ 87 | * ██░┃░ ░┃░┃░ ░┃░██ ░┃░┃░ 88 | * ██━┛░ ░┗━┛░ ░┗━██ █████ 89 | * ██░░░ ░░░░░ ░░░██ █████ 90 | * 91 | * Fully surrounded case (4-axis crossing): 92 | * 93 | * █████ 94 | * █┏━┓█ 95 | * █┃█┃█ 96 | * █┗━┛█ 97 | * █████ 98 | */ 99 | 100 | none = 0, 101 | 102 | inner = (1 << 1), 103 | north = (1 << 2), 104 | east = (1 << 3), 105 | south = (1 << 4), 106 | west = (1 << 5), 107 | 108 | north_east = north | east, 109 | north_west = north | west, 110 | south_east = south | east, 111 | south_west = south | west, 112 | 113 | north_south = north | south, 114 | east_west = east | west, 115 | 116 | left = south | west | north, 117 | top = west | north | east, 118 | right = north | east | south, 119 | bottom = east | south | west, 120 | surrounded = north | east | south | west, 121 | 122 | inner_north = inner | north, 123 | inner_north_east = inner | north | east, 124 | inner_east = inner | east, 125 | inner_south_east = inner | south | east, 126 | inner_south = inner | south, 127 | inner_south_west = inner | south | west, 128 | inner_west = inner | west, 129 | inner_north_west = inner | north | west, 130 | 131 | inner_north_south = inner | north | south, 132 | inner_east_west = inner | east | west, 133 | 134 | inner_left = inner | south | west | north, 135 | inner_top = inner | west | north | east, 136 | inner_right = inner | north | east | south, 137 | inner_bottom = inner | east | south | west, 138 | inner_surrounded = inner | north | east | south | west, 139 | }; 140 | 141 | intersect_2d intersect(rect_2d r1, rect_2d r2) 142 | { 143 | int r1_p0_x_lt_r2_p0_x = r1.p0.x < r2.p0.x; 144 | int r1_p0_x_lt_r2_p1_x = r1.p0.x < r2.p1.x; 145 | int r1_p1_x_lt_r2_p0_x = r1.p1.x < r2.p0.x; 146 | int r1_p1_x_lt_r2_p1_x = r1.p1.x < r2.p1.x; 147 | int r1_p0_y_lt_r2_p0_y = r1.p0.y < r2.p0.y; 148 | int r1_p0_y_lt_r2_p1_y = r1.p0.y < r2.p1.y; 149 | int r1_p1_y_lt_r2_p0_y = r1.p1.y < r2.p0.y; 150 | int r1_p1_y_lt_r2_p1_y = r1.p1.y < r2.p1.y; 151 | 152 | int p0_x_lt = r1_p0_x_lt_r2_p0_x && r1_p0_x_lt_r2_p1_x; 153 | int p0_x_in = !r1_p0_x_lt_r2_p0_x && r1_p0_x_lt_r2_p1_x; 154 | int p0_x_ge = !r1_p0_x_lt_r2_p0_x && !r1_p0_x_lt_r2_p1_x; 155 | 156 | int p1_x_lt = r1_p1_x_lt_r2_p0_x && r1_p1_x_lt_r2_p1_x; 157 | int p1_x_in = !r1_p1_x_lt_r2_p0_x && r1_p1_x_lt_r2_p1_x; 158 | int p1_x_ge = !r1_p1_x_lt_r2_p0_x && !r1_p1_x_lt_r2_p1_x; 159 | 160 | int p0_y_lt = r1_p0_y_lt_r2_p0_y && r1_p0_y_lt_r2_p1_y; 161 | int p0_y_in = !r1_p0_y_lt_r2_p0_y && r1_p0_y_lt_r2_p1_y; 162 | int p0_y_ge = !r1_p0_y_lt_r2_p0_y && !r1_p0_y_lt_r2_p1_y; 163 | 164 | int p1_y_lt = r1_p1_y_lt_r2_p0_y && r1_p1_y_lt_r2_p1_y; 165 | int p1_y_in = !r1_p1_y_lt_r2_p0_y && r1_p1_y_lt_r2_p1_y; 166 | int p1_y_ge = !r1_p1_y_lt_r2_p0_y && !r1_p1_y_lt_r2_p1_y; 167 | 168 | /* Outside cases: 169 | * 170 | * ░░░░░ ░░░░░ ░░░░░ █░░░░ ░░█░░ ░░░░█ ░░░░░ ░░░░░ 171 | * ░┏━┓░ ░┏━┓░ ░┏━┓░ ░┏━┓░ ░┏━┓░ ░┏━┓░ ░┏━┓░ ░┏━┓░ 172 | * ░┃░┃░ ░┃░┃░ █┃░┃░ ░┃░┃░ ░┃░┃░ ░┃░┃░ ░┃░┃█ ░┃░┃░ 173 | * ░┗━┛░ ░┗━┛░ ░┗━┛░ ░┗━┛░ ░┗━┛░ ░┗━┛░ ░┗━┛░ ░┗━┛░ 174 | * ░░█░░ █░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░█ 175 | */ 176 | unsigned out = 0; 177 | out |= (p0_y_lt && p1_y_lt) ? intersect_2d::north : 0; 178 | out |= (p0_x_ge && p1_x_ge) ? intersect_2d::east : 0; 179 | out |= (p0_y_ge && p1_y_ge) ? intersect_2d::south : 0; 180 | out |= (p0_x_lt && p1_x_lt) ? intersect_2d::west : 0; 181 | if (out) return (intersect_2d)out; 182 | 183 | /* Fully inside case (0-axis crossing): 184 | * 185 | * ░░░░░ 186 | * ░┏━┓░ 187 | * ░┃█┃░ 188 | * ░┗━┛░ 189 | * ░░░░░ 190 | */ 191 | if (p0_x_in && p1_x_in && p0_y_in && p1_y_in) return intersect_2d::inner; 192 | 193 | /* Overlap cases (1-axis crossing): 194 | * 195 | * ░░░░░ ░░░░░ ░░█░░ ░░░░░ 196 | * ░┏━┓░ ░┏━┓░ ░┏█┓░ ░┏━┓░ 197 | * ░┃░┃░ ██░┃░ ░┃░┃░ ░┃░██ 198 | * ░┗█┛░ ░┗━┛░ ░┗━┛░ ░┗━┛░ 199 | * ░░█░░ ░░░░░ ░░░░░ ░░░░░ 200 | */ 201 | else if (p0_x_in && p1_x_in && p0_y_in && p1_y_ge) return intersect_2d::inner_south; 202 | else if (p0_x_lt && p1_x_in && p0_y_in && p1_y_in) return intersect_2d::inner_west; 203 | else if (p0_x_in && p1_x_in && p0_y_lt && p1_y_in) return intersect_2d::inner_north; 204 | else if (p0_x_in && p1_x_ge && p0_y_in && p1_y_in) return intersect_2d::inner_east; 205 | 206 | /* Overlap cases (2-axis crossing): 207 | * 208 | * ░░░░░ ██░░░ ░░░██ ░░░░░ ░░█░░ ░░░░░ 209 | * ░┏━┓░ ██━┓░ ░┏━██ ░┏━┓░ ░┏█┓░ ░┏━┓░ 210 | * ░┃░┃░ ░┃░┃░ ░┃░┃░ ░┃░┃░ ░┃█┃░ █████ 211 | * ██━┛░ ░┗━┛░ ░┗━┛░ ░┗━██ ░┗█┛░ ░┗━┛░ 212 | * ██░░░ ░░░░░ ░░░░░ ░░░██ ░░█░░ ░░░░░ 213 | */ 214 | else if (p0_x_lt && p1_x_in && p0_y_in && p1_y_ge) return intersect_2d::inner_south_west; 215 | else if (p0_x_lt && p1_x_in && p0_y_lt && p1_y_in) return intersect_2d::inner_north_west; 216 | else if (p0_x_in && p1_x_ge && p0_y_lt && p1_y_in) return intersect_2d::inner_north_east; 217 | else if (p0_x_in && p1_x_ge && p0_y_in && p1_y_ge) return intersect_2d::inner_south_east; 218 | else if (p0_x_in && p1_x_in && p0_y_lt && p1_y_ge) return intersect_2d::inner_north_south; 219 | else if (p0_x_lt && p1_x_ge && p0_y_in && p1_y_in) return intersect_2d::inner_east_west; 220 | 221 | /* Overlap cases (3-axis crossing): 222 | * 223 | * ██░░░ █████ ░░░██ ░░░░░ 224 | * ██━┓░ █████ ░┏━██ ░┏━┓░ 225 | * ██░┃░ ░┃░┃░ ░┃░██ ░┃░┃░ 226 | * ██━┛░ ░┗━┛░ ░┗━██ █████ 227 | * ██░░░ ░░░░░ ░░░██ █████ 228 | */ 229 | else if (p0_x_lt && p1_x_in && p0_y_lt && p1_y_ge) return intersect_2d::inner_left; 230 | else if (p0_x_lt && p1_x_ge && p0_y_lt && p1_y_in) return intersect_2d::inner_top; 231 | else if (p0_x_in && p1_x_ge && p0_y_lt && p1_y_ge) return intersect_2d::inner_right; 232 | else if (p0_x_lt && p1_x_ge && p0_y_in && p1_y_ge) return intersect_2d::inner_bottom; 233 | 234 | /* Fully surrounded case (4-axis crossing): 235 | * 236 | * █████ 237 | * █┏━┓█ 238 | * █┃█┃█ 239 | * █┗━┛█ 240 | * █████ 241 | */ 242 | else if (p0_x_lt && p1_x_ge && p0_y_lt && p1_y_ge) return intersect_2d::inner_surrounded; 243 | else return intersect_2d::none; 244 | } 245 | -------------------------------------------------------------------------------- /src/glyph.h: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #pragma once 4 | 5 | #include "glm/glm.hpp" 6 | #include "glm/gtc/matrix_inverse.hpp" 7 | 8 | /* 9 | * FreeType Span Measurement 10 | * 11 | * Measures minimum and maximum x and y coordinates for one glyph. 12 | * Used as a callback to FT_Outline_Render. 13 | */ 14 | 15 | struct span_measure 16 | { 17 | int min_x, min_y, max_x, max_y; 18 | 19 | span_measure(); 20 | 21 | static void fn(int y, int count, const FT_Span* spans, void *user); 22 | }; 23 | 24 | inline span_measure::span_measure() : 25 | min_x(INT_MAX), min_y(INT_MAX), max_x(INT_MIN), max_y(INT_MIN) {} 26 | 27 | 28 | /* 29 | * FreeType Span Recorder 30 | * 31 | * Collects the output of span coverage into an 8-bit grayscale bitmap. 32 | * Used as a callback to FT_Outline_Render. 33 | */ 34 | 35 | struct span_vector : span_measure 36 | { 37 | int gx, gy, ox, oy, w, h; 38 | 39 | std::vector pixels; 40 | 41 | span_vector(); 42 | 43 | void reset(int width, int height); 44 | 45 | static void fn(int y, int count, const FT_Span* spans, void *user); 46 | }; 47 | 48 | inline span_vector::span_vector() : 49 | gx(0), gy(0), ox(0), oy(0), w(0), h(0), pixels() {} 50 | 51 | 52 | /* 53 | * Font Atlas Entry 54 | * 55 | * Holds the details for an entry in the Font Atlas glyph map. 56 | */ 57 | 58 | struct atlas_entry 59 | { 60 | int bin_id, font_size; 61 | short x, y, ox, oy, w, h; 62 | float uv[4]; 63 | 64 | atlas_entry() = default; 65 | atlas_entry(int bin_id); 66 | atlas_entry(int bin_id, int font_size, int x, int y, int ox, int oy, 67 | int w, int h, const float uv[4]); 68 | }; 69 | 70 | inline atlas_entry::atlas_entry(int bin_id) : 71 | bin_id(bin_id), font_size(0), x(0), y(0), ox(0), oy(0), 72 | w(0), h(0), uv{0} {} 73 | 74 | inline atlas_entry::atlas_entry(int bin_id, int font_size, int x, int y, 75 | int ox, int oy, int w, int h, const float uv[4]) : 76 | bin_id(bin_id), font_size(font_size), x(x), y(y), ox(ox), oy(oy), 77 | w(w), h(h), uv{uv[0], uv[1], uv[2], uv[3]} {} 78 | 79 | /* 80 | * Font Atlas 81 | * 82 | * Font Atlas implementation that uses the MaxRext-BSSF bin packer. 83 | * The Font Atlas is graphics API agnostic. It is necessary for client 84 | * code to update an atlas texture after text has been rendered. 85 | */ 86 | 87 | struct glyph_renderer; 88 | 89 | struct font_atlas 90 | { 91 | size_t width, height, depth; 92 | std::map glyph_map; 93 | uint8_t *pixels; 94 | float uv1x1; 95 | bin_packer bp; 96 | bin_rect delta; 97 | std::atomic multithreading; 98 | std::mutex mutex; 99 | std::shared_ptr img; 100 | 101 | static const int PADDING = 1; 102 | static const int DEFAULT_WIDTH = 1024; 103 | static const int DEFAULT_HEIGHT = 1024; 104 | static const int GRAY_DEPTH = 1; 105 | static const int COLOR_DEPTH = 4; 106 | static const int MSDF_DEPTH = 4; 107 | 108 | font_atlas(); 109 | font_atlas(size_t width, size_t height, size_t depth); 110 | ~font_atlas(); 111 | 112 | /* returns backing image */ 113 | image* get_image(); 114 | 115 | /* internal interfaces */ 116 | void init(); 117 | void create_pixels(); 118 | void clear_pixels(); 119 | void uv_pixel(); 120 | void reset_bins(); 121 | void reset(size_t width, size_t height, size_t depth); 122 | 123 | /* interface used by text_renderer and glyph_renderer */ 124 | atlas_entry resize(font_face *face, int font_size, int glyph, 125 | atlas_entry *tmpl); 126 | atlas_entry lookup(font_face *face, int font_size, int glyph, 127 | glyph_renderer *renderer); 128 | atlas_entry create(font_face *face, int font_size, int glyph, 129 | int entry_font_size, int ox, int oy, int w, int h); 130 | 131 | /* create entry uvs */ 132 | void create_uvs(float uv[4], bin_rect r); 133 | 134 | /* tracking minimum required update rectangle */ 135 | bin_rect get_delta(); 136 | void expand_delta(bin_rect b); 137 | 138 | /* persistance */ 139 | enum file_type { 140 | ttf_file, 141 | csv_file, 142 | png_file, 143 | }; 144 | std::string get_path(font_face *face, file_type type); 145 | void save_map(font_manager *manager, font_face *face, FILE *out); 146 | void load_map(font_manager *manager, font_face *face, FILE *in); 147 | void save(font_manager *manager, font_face *face); 148 | void load(font_manager *manager, font_face *face); 149 | }; 150 | 151 | inline int atlas_image_filter(font_atlas *atlas) 152 | { 153 | /* 154 | * todo fixme - we currently use depth as an indicator of type 155 | * 156 | * Grey bitmaps need nearest as they are 1:1 157 | * MSDF bitmaps need linear as they are distances 158 | * Emoji bitmaps may need nearest or linear 159 | */ 160 | 161 | //return (atlas->depth == 4 ? filter_linear : filter_nearest); 162 | return filter_linear; 163 | } 164 | 165 | /* 166 | * Text Segment 167 | * 168 | * Structure to hold a segment of text that uses the same font, font size, 169 | * and color. Also contains coordinates used by the Text Renderer when 170 | * outputting vertices. 171 | */ 172 | 173 | struct text_segment 174 | { 175 | std::string text; 176 | std::string language; 177 | font_face *face; 178 | int font_size; 179 | float x, y; 180 | float baseline_shift; 181 | float line_height; 182 | float tracking; 183 | uint32_t color; 184 | 185 | text_segment() = default; 186 | text_segment(std::string text, std::string languag); 187 | text_segment(std::string text, std::string language, font_face *face, 188 | int font_size, float x, float y, uint32_t color); 189 | }; 190 | 191 | inline text_segment::text_segment(std::string text, std::string language) : 192 | text(text), language(language), face(nullptr), font_size(0), 193 | x(0), y(0), baseline_shift(0), line_height(0), tracking(0), color(0) {} 194 | 195 | inline text_segment::text_segment(std::string text, std::string language, 196 | font_face *face, int font_size, float x, float y, uint32_t color) : 197 | text(text), language(language), face(face), font_size(font_size), 198 | x(x), y(y), baseline_shift(0), line_height(0), tracking(0), color(color) {} 199 | 200 | 201 | /* 202 | * Glyph Shape 203 | * 204 | * Structure to hold the output of the text shaper. 205 | */ 206 | 207 | struct glyph_shape 208 | { 209 | unsigned glyph; /* glyph index for the chosen font */ 210 | unsigned cluster; /* offset within original string */ 211 | int x_offset, y_offset; /* integer with 6 fraction bits */ 212 | int x_advance, y_advance; /* integer with 6 fraction bits */ 213 | glm::vec3 pos[2]; 214 | }; 215 | 216 | 217 | /* 218 | * Text Shapers 219 | * 220 | * The shaper takes the font face, font size and text fromt a Text Segment 221 | * and calculates the position of each character. The Text Shaper output is 222 | * fed into the Text Renderer. This decomposition allows measurement of a 223 | * text segment before rendering it. Thhere are multiple implementations: 224 | * 225 | * - text_shaper_ft - FreeType based text shaper with round to integer kerning. 226 | * (suitable for monospaced fonts) 227 | * - text_shaper_hb - HarfBuzz based text shaper with round to integer kerning. 228 | * (sutable for any fonts) 229 | * 230 | * Note: the Freetype Shaper is not measurably faster than the HarfBuzz shaper, 231 | * although it could be made faster with google dense hashmap. The main benefit 232 | * is that it elminates a dependency. 233 | */ 234 | 235 | struct text_shaper 236 | { 237 | virtual void shape(std::vector &shapes, text_segment &segment) = 0; 238 | }; 239 | 240 | struct text_shaper_ft : text_shaper 241 | { 242 | virtual void shape(std::vector &shapes, text_segment &segment); 243 | }; 244 | 245 | struct text_shaper_hb : text_shaper 246 | { 247 | virtual void shape(std::vector &shapes, text_segment &segment); 248 | }; 249 | 250 | 251 | /* 252 | * Glyph Renderer 253 | * 254 | * Implementation of a simple freetype based glyph renderer. The output 255 | * of the glyph renderer is a bitmap which is stored in a font atlas. 256 | */ 257 | 258 | struct glyph_renderer 259 | { 260 | virtual ~glyph_renderer() = default; 261 | 262 | virtual atlas_entry render(font_atlas* atlas, font_face_ft *face, 263 | int font_size, int glyph) = 0; 264 | }; 265 | 266 | struct glyph_renderer_outline_ft : glyph_renderer 267 | { 268 | span_vector span; 269 | 270 | glyph_renderer_outline_ft() = default; 271 | virtual ~glyph_renderer_outline_ft() = default; 272 | 273 | atlas_entry render(font_atlas* atlas, font_face_ft *face, 274 | int font_size, int glyph); 275 | }; 276 | 277 | struct glyph_renderer_color_ft : glyph_renderer 278 | { 279 | span_vector span; 280 | 281 | glyph_renderer_color_ft() = default; 282 | virtual ~glyph_renderer_color_ft() = default; 283 | 284 | atlas_entry render(font_atlas* atlas, font_face_ft *face, 285 | int font_size, int glyph); 286 | }; 287 | 288 | 289 | /* 290 | * Text Renderer 291 | * 292 | * Implementation of a simple freetype based text renderer. The output 293 | * of the text renderer is an array of vertices and triangle indices 294 | * The output is graphics API agnostic and can be utilized by DirectX, 295 | * OpenGL, Metal or Vulkan. 296 | */ 297 | 298 | struct text_renderer 299 | { 300 | static const bool debug = false; 301 | 302 | virtual ~text_renderer() = default; 303 | 304 | virtual void render(draw_list &batch, 305 | std::vector &shapes, 306 | text_segment &segment, glm::mat3 m = glm::mat3(1)) = 0; 307 | }; 308 | 309 | struct text_renderer_ft : text_renderer 310 | { 311 | font_manager* manager; 312 | std::unique_ptr renderer; 313 | 314 | text_renderer_ft(font_manager* manager); 315 | virtual ~text_renderer_ft() = default; 316 | 317 | void render(draw_list &batch, 318 | std::vector &shapes, 319 | text_segment &segment, glm::mat3 m = glm::mat3(1)); 320 | }; 321 | 322 | inline text_renderer_ft::text_renderer_ft(font_manager* manager) : 323 | manager(manager) {} -------------------------------------------------------------------------------- /src/image.h: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #pragma once 4 | 5 | typedef unsigned int uint; 6 | typedef unsigned short ushort; 7 | 8 | struct image; 9 | typedef std::shared_ptr image_ptr; 10 | 11 | struct file; 12 | typedef std::shared_ptr file_ptr; 13 | 14 | struct image_io; 15 | struct image_io_png; 16 | 17 | /* pixel_format */ 18 | 19 | enum pixel_format 20 | { 21 | pixel_format_none, 22 | pixel_format_alpha, 23 | pixel_format_rgb, 24 | pixel_format_rgba, 25 | pixel_format_argb, 26 | pixel_format_rgb555, 27 | pixel_format_rgb565, 28 | pixel_format_luminance, 29 | pixel_format_luminance_alpha, 30 | }; 31 | 32 | /* image */ 33 | 34 | struct image 35 | { 36 | public: 37 | static const char* formatname[]; 38 | 39 | static image_io_png PNG; 40 | 41 | static int iid_seq; 42 | 43 | int iid; 44 | file_ptr rsrc; 45 | uint width, height; 46 | pixel_format format; 47 | uint8_t *pixels; 48 | bool ownData; 49 | 50 | image(); 51 | image(const image &image); 52 | image(file_ptr rsrc, uint width, uint height, 53 | pixel_format format = pixel_format_rgba, uint8_t *pixels = nullptr); 54 | 55 | virtual ~image(); 56 | 57 | static uint getBytesPerPixel(pixel_format format) 58 | { 59 | switch (format) { 60 | case pixel_format_none: return 0; 61 | case pixel_format_alpha: return 1; 62 | case pixel_format_rgb: return 3; 63 | case pixel_format_rgba: return 4; 64 | case pixel_format_argb: return 4; 65 | case pixel_format_rgb555: return 2; 66 | case pixel_format_rgb565: return 2; 67 | case pixel_format_luminance: return 1; 68 | case pixel_format_luminance_alpha: return 2; 69 | default: return 0; 70 | } 71 | } 72 | 73 | uint getBytesPerPixel() { return getBytesPerPixel(format); } 74 | uint getWidth() { return width; } 75 | uint getHeight() { return height; } 76 | uint8_t* getData() { return pixels; } 77 | pixel_format getpixel_format() { return format; } 78 | uint8_t* move() { ownData = false; return pixels; } 79 | 80 | void create(pixel_format format, uint width, uint height); 81 | void convertFormat(pixel_format newformat); 82 | 83 | static image_io* getImageIO(unsigned char magic[8]); 84 | static image_io* getImageIO(std::string filename); 85 | static image_io* getImageIO(file_ptr rsrc); 86 | static image_io* getImageIOFromExt(std::string pathname); 87 | static void saveToFile(std::string filename, 88 | const image_ptr &image, image_io *imageio = NULL); 89 | static image_ptr createBitmap(uint width, uint height, 90 | pixel_format format, uint8_t *pixels = NULL); 91 | static image_ptr createFromFile(std::string filename, 92 | image_io *imageio = NULL, pixel_format optformat = pixel_format_none); 93 | static image_ptr createFromResouce(std::string rsrcName, 94 | image_io *imageio = NULL, pixel_format optformat = pixel_format_none); 95 | static image_ptr createFromResouce(file_ptr rsrc, 96 | image_io *imageio = NULL, pixel_format optformat = pixel_format_none); 97 | }; 98 | 99 | inline image::image() : 100 | iid(++iid_seq), rsrc(), width(0), height(0), format(pixel_format_none), 101 | pixels(nullptr), ownData(false) {} 102 | 103 | inline image::image(const image &image) : 104 | iid(++iid_seq), rsrc(image.rsrc), width(image.width), height(image.height), 105 | format(image.format), ownData(false) 106 | { 107 | create(format, width, height); 108 | memcpy(pixels, image.pixels, (size_t)width * height * getBytesPerPixel()); 109 | } 110 | 111 | inline image::image(file_ptr rsrc, uint width, uint height, 112 | pixel_format format, uint8_t *pixels) : iid(++iid_seq), rsrc(rsrc), 113 | width(width), height(height), format(format), pixels(pixels), ownData(false) 114 | { 115 | if (!pixels) { 116 | create(format, width, height); 117 | } 118 | } 119 | 120 | /* image_io */ 121 | 122 | struct image_io 123 | { 124 | public: 125 | virtual image* load(file_ptr rsrc, pixel_format optformat) = 0; 126 | virtual void save(image* image, std::string filename) = 0; 127 | 128 | virtual ~image_io() {} 129 | }; 130 | 131 | struct image_io_png : public image_io 132 | { 133 | public: 134 | image* load(file_ptr rsrc, pixel_format optformat); 135 | void save(image* image, std::string filename); 136 | }; 137 | -------------------------------------------------------------------------------- /src/logger.cc: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include "logger.h" 15 | 16 | #ifdef _WIN32 17 | #include 18 | #endif 19 | 20 | logger::L logger::level = logger::L::Linfo; 21 | 22 | const char* logger::level_names[6] = { "trace", "debug", "info", "warn", "error", "panic" }; 23 | 24 | void logger::output(const char *prefix, const char* fmt, va_list args1) 25 | { 26 | std::vector buf; 27 | va_list args2; 28 | size_t plen, pout, len, ret; 29 | 30 | va_copy(args2, args1); 31 | 32 | plen = strlen(prefix); 33 | len = (size_t)vsnprintf(NULL, 0, fmt, args1); 34 | assert(len >= 0); 35 | buf.resize(plen + len + 4); // prefix + ": " + message + CR + zero-terminator 36 | pout = (size_t)snprintf(buf.data(), buf.capacity(), "%s: ", prefix); 37 | ret = (size_t)vsnprintf(buf.data() + pout, buf.capacity(), fmt, args2); 38 | assert(len == ret); 39 | if (buf[buf.size()-3] != '\n') buf[buf.size()-2] = '\n'; 40 | 41 | #if defined (_WIN32) && !defined (_CONSOLE) 42 | OutputDebugStringA(buf.data()); 43 | #else 44 | printf("%s", buf.data()); 45 | #endif 46 | } 47 | 48 | void logger::log(L level, const char* fmt, ...) 49 | { 50 | va_list ap; 51 | va_start(ap, fmt); 52 | if (level >= logger::level) { 53 | output(logger::level_names[level], fmt, ap); 54 | } 55 | va_end(ap); 56 | } 57 | 58 | void logger::set_level(L level) 59 | { 60 | logger::level = level; 61 | } -------------------------------------------------------------------------------- /src/logger.h: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #pragma once 4 | 5 | struct logger 6 | { 7 | enum L { Ltrace, Ldebug, Linfo, Lwarn, Lerror, Lpanic }; 8 | 9 | static const char* level_names[6]; 10 | 11 | static L level; 12 | 13 | static void output(const char*prefix, const char* fmt, va_list ap); 14 | static void log(L level, const char* fmt, ...); 15 | static void set_level(L level); 16 | }; 17 | 18 | #define Trace(fmt, ...) \ 19 | if (logger::L::Ltrace >= logger::level) { logger::log(logger::L::Ldebug, fmt, __VA_ARGS__); } 20 | 21 | #define Debug(fmt, ...) \ 22 | if (logger::L::Ldebug >= logger::level) { logger::log(logger::L::Ldebug, fmt, __VA_ARGS__); } 23 | 24 | #define Info(fmt, ...) \ 25 | logger::log(logger::L::Linfo, fmt, __VA_ARGS__); 26 | 27 | #define Warn(fmt, ...) \ 28 | logger::log(logger::L::Lwarn, fmt, __VA_ARGS__); 29 | 30 | #define Error(fmt, ...) \ 31 | logger::log(logger::L::Lerror, fmt, __VA_ARGS__); 32 | 33 | #define Panic(fmt, ...) \ 34 | logger::log(logger::L::Lpanic, fmt, __VA_ARGS__); 35 | -------------------------------------------------------------------------------- /src/msdf.cc: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include FT_FREETYPE_H 23 | #include FT_MODULE_H 24 | #include FT_GLYPH_H 25 | #include FT_OUTLINE_H 26 | 27 | #include "core/arithmetics.hpp" 28 | #include "core/Vector2.h" 29 | #include "core/Scanline.h" 30 | #include "core/Shape.h" 31 | #include "core/BitmapRef.hpp" 32 | #include "core/Bitmap.h" 33 | #include "core/pixel-conversion.hpp" 34 | #include "core/edge-coloring.h" 35 | #include "core/render-sdf.h" 36 | #include "core/rasterization.h" 37 | #include "core/estimate-sdf-error.h" 38 | #include "core/save-bmp.h" 39 | #include "core/save-tiff.h" 40 | #include "core/shape-description.h" 41 | #include "ext/save-png.h" 42 | #include "ext/import-svg.h" 43 | #include "ext/import-font.h" 44 | #include "msdfgen.h" 45 | 46 | #include "binpack.h" 47 | #include "utf8.h" 48 | #include "image.h" 49 | #include "draw.h" 50 | #include "font.h" 51 | #include "glyph.h" 52 | #include "msdf.h" 53 | 54 | typedef unsigned uint; 55 | 56 | struct FtContext { 57 | msdfgen::Shape *shape; 58 | msdfgen::Point2 position; 59 | msdfgen::Contour *contour; 60 | }; 61 | 62 | static msdfgen::Point2 ftPoint2(const FT_Vector &vector) { 63 | return msdfgen::Point2(vector.x/64., vector.y/64.); 64 | } 65 | 66 | static int ftMoveTo(const FT_Vector *to, void *user) { 67 | FtContext *context = static_cast(user); 68 | context->contour = &context->shape->addContour(); 69 | context->position = ftPoint2(*to); 70 | return 0; 71 | } 72 | 73 | static int ftLineTo(const FT_Vector *to, void *user) { 74 | FtContext *context = static_cast(user); 75 | context->contour->addEdge(new msdfgen::LinearSegment(context->position, 76 | ftPoint2(*to))); 77 | context->position = ftPoint2(*to); 78 | return 0; 79 | } 80 | 81 | static int ftConicTo(const FT_Vector *control, const FT_Vector *to, void *user) { 82 | FtContext *context = static_cast(user); 83 | context->contour->addEdge(new msdfgen::QuadraticSegment(context->position, 84 | ftPoint2(*control), ftPoint2(*to))); 85 | context->position = ftPoint2(*to); 86 | return 0; 87 | } 88 | 89 | static int ftCubicTo(const FT_Vector *control1, const FT_Vector *control2, 90 | const FT_Vector *to, void *user) { 91 | FtContext *context = static_cast(user); 92 | context->contour->addEdge(new msdfgen::CubicSegment(context->position, 93 | ftPoint2(*control1), ftPoint2(*control2), ftPoint2(*to))); 94 | context->position = ftPoint2(*to); 95 | return 0; 96 | } 97 | 98 | 99 | /* 100 | * glyph_renderer_msdf 101 | */ 102 | 103 | atlas_entry glyph_renderer_msdf::render(font_atlas *atlas, font_face_ft *face, 104 | int font_size, int glyph) 105 | { 106 | msdfgen::Shape shape; 107 | msdfgen::Vector2 translate, scale = { 1, 1 }; 108 | FT_GlyphSlot ftglyph; 109 | FT_Error error; 110 | FT_Outline_Funcs ftFunctions; 111 | FtContext context = { &shape }; 112 | atlas_entry ae; 113 | 114 | int char_height = 128 * 64; /* magic - shader uses textureSize() */ 115 | int horz_resolution = font_manager::dpi; 116 | double range = 8; 117 | bool overlapSupport = true; 118 | bool scanlinePass = true; 119 | double angleThreshold = 3; 120 | double edgeThreshold = 1.001; 121 | uint coloringSeed = 0; 122 | msdfgen::FillRule fillRule = msdfgen::FILL_NONZERO; 123 | 124 | error = FT_Set_Char_Size(face->ftface, 0, char_height, horz_resolution, 125 | horz_resolution); 126 | if (error) { 127 | return atlas_entry(-1); 128 | } 129 | error = FT_Load_Glyph(face->ftface, glyph, FT_LOAD_NO_HINTING); 130 | if (error) { 131 | return atlas_entry(-1); 132 | } 133 | 134 | ftFunctions.move_to = ftMoveTo; 135 | ftFunctions.line_to = ftLineTo; 136 | ftFunctions.conic_to = ftConicTo; 137 | ftFunctions.cubic_to = ftCubicTo; 138 | ftFunctions.shift = 0; 139 | ftFunctions.delta = 0; 140 | 141 | error = FT_Outline_Decompose(&face->ftface->glyph->outline, &ftFunctions, 142 | &context); 143 | if (error) { 144 | return atlas_entry(-1); 145 | } 146 | 147 | /* font dimensions */ 148 | ftglyph = face->ftface->glyph; 149 | int ox = (int)floorf((float)ftglyph->metrics.horiBearingX / 64.0f) - 1; 150 | int oy = (int)floorf((float)(ftglyph->metrics.horiBearingY - 151 | ftglyph->metrics.height) / 64.0f) - 1; 152 | int w = (int)ceilf(ftglyph->metrics.width / 64.0f) + 2; 153 | int h = (int)ceilf(ftglyph->metrics.height / 64.0f) + 2; 154 | translate.x = -ox; 155 | translate.y = -oy; 156 | 157 | msdfgen::Bitmap msdf(w, h); 158 | msdfgen::edgeColoringSimple(shape, angleThreshold, coloringSeed); 159 | msdfgen::generateMSDF(msdf, shape, range, scale, translate, 160 | scanlinePass ? 0 : edgeThreshold, overlapSupport); 161 | msdfgen::distanceSignCorrection(msdf, shape, scale, translate, fillRule); 162 | if (edgeThreshold > 0) { 163 | msdfgen::msdfErrorCorrection(msdf, edgeThreshold/(scale*range)); 164 | } 165 | 166 | /* create atlas entry and copy rasterized glyph into the atlas */ 167 | ae = atlas->create(face, 0, glyph, char_height, ox, oy, w, h); 168 | if (ae.bin_id >= 0) { 169 | for (int x = 0; x < w; x++) { 170 | for (int y = 0; y < h; y++) { 171 | int r = msdfgen::pixelFloatToByte(msdf(x,y)[0]); 172 | int g = msdfgen::pixelFloatToByte(msdf(x,y)[1]); 173 | int b = msdfgen::pixelFloatToByte(msdf(x,y)[2]); 174 | size_t dst = ((ae.y + y) * atlas->width + ae.x + x) * 4; 175 | uint32_t color = r | g << 8 | b << 16 | 0xff000000; 176 | *(uint32_t*)&atlas->pixels[dst] = color; 177 | } 178 | } 179 | } 180 | 181 | /* clients expect font metrics for the font size to be loaded */ 182 | face->get_metrics(font_size); 183 | 184 | /* 185 | * note: we recurse into the font atlas lookup code to trigger 186 | * generation of an atlas entry for the requested font size. 187 | */ 188 | return ae; 189 | } -------------------------------------------------------------------------------- /src/msdf.h: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #pragma once 4 | 5 | struct glyph_renderer_msdf : glyph_renderer 6 | { 7 | span_vector span; 8 | 9 | glyph_renderer_msdf() = default; 10 | virtual ~glyph_renderer_msdf() = default; 11 | 12 | atlas_entry render(font_atlas* atlas, font_face_ft *face, 13 | int font_size, int glyph); 14 | }; 15 | -------------------------------------------------------------------------------- /src/multi.cc: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include FT_FREETYPE_H 25 | #include FT_MODULE_H 26 | #include FT_GLYPH_H 27 | #include FT_OUTLINE_H 28 | 29 | #include "binpack.h" 30 | #include "utf8.h" 31 | #include "image.h" 32 | #include "draw.h" 33 | #include "font.h" 34 | #include "glyph.h" 35 | #include "multi.h" 36 | #include "logger.h" 37 | 38 | /* 39 | * glyph_renderer_worker 40 | */ 41 | 42 | static const char log_name[] = "glyph_renderer_worker"; 43 | 44 | void glyph_renderer_worker::mainloop() 45 | { 46 | std::unique_ptr renderer = renderer_factory.create(); 47 | 48 | while (master->running) { 49 | size_t total, processing, workitem, processed; 50 | 51 | total = master->total.load(std::memory_order_acquire); 52 | processing = master->processing.load(std::memory_order_acquire); 53 | 54 | if (processing >= total) { 55 | std::unique_lock lock(master->mutex); 56 | master->request.wait(lock); 57 | continue; 58 | } 59 | 60 | workitem = master->processing.fetch_add(1, std::memory_order_seq_cst); 61 | glyph_render_request r = master->queue[workitem]; 62 | r.atlas->multithreading.store(true, std::memory_order_release); 63 | renderer->render(r.atlas, get_face(r.face), 0, r.glyph); 64 | processed = master->processed.fetch_add(1, std::memory_order_seq_cst); 65 | 66 | if (debug) { 67 | Debug("%s-%02zu workitem=%zu\t[font=%s, glyph=%d]\n", 68 | log_name, worker_num, workitem, r.face->name.c_str(), 69 | r.glyph); 70 | } 71 | 72 | total = master->total.load(std::memory_order_acquire); 73 | if (processed == total - 1) { 74 | if (debug) { 75 | Debug("%s-%02zu notify-master\n", log_name, worker_num); 76 | } 77 | master->response.notify_one(); 78 | } 79 | } 80 | } 81 | 82 | font_face_ft* glyph_renderer_worker::get_face(font_face_ft *face) 83 | { 84 | auto fi = face_map.find(face); 85 | if (fi != face_map.end()) { 86 | return fi->second.get(); 87 | } else { 88 | fi = face_map.insert(face_map.end(), std::make_pair(face, 89 | std::unique_ptr(face->dup_thread()))); 90 | return fi->second.get(); 91 | } 92 | } 93 | 94 | /* 95 | * glyph_renderer_multi 96 | */ 97 | 98 | glyph_renderer_multi::glyph_renderer_multi(font_manager* manager, 99 | glyph_renderer_factory& renderer_factory, size_t num_threads) : 100 | variable_size(true), manager(manager), 101 | workers(), running(true), dedup(), queue(), 102 | capacity(1024), total(0), processing(0), processed(0), 103 | mutex(), request(), response() 104 | { 105 | for (size_t i = 0; i < num_threads; i++) { 106 | workers.push_back(std::make_unique 107 | (this, i, renderer_factory)); 108 | } 109 | queue.resize(1024); 110 | } 111 | 112 | glyph_renderer_multi::~glyph_renderer_multi() 113 | { 114 | shutdown(); 115 | } 116 | 117 | void glyph_renderer_multi::add(std::vector &shapes, 118 | text_segment *segment) 119 | { 120 | font_face_ft *face = static_cast(segment->face); 121 | int font_size = segment->font_size; 122 | font_atlas *atlas = manager->getCurrentAtlas(face); 123 | 124 | for (auto shape : shapes) { 125 | auto gi = atlas->glyph_map.find({face->font_id, 0, shape.glyph}); 126 | if (gi != atlas->glyph_map.end()) continue; 127 | 128 | glyph_render_request r{atlas, face, shape.glyph}; 129 | auto i = std::lower_bound(dedup.begin(), dedup.end(), r, 130 | [](const glyph_render_request &l, 131 | const glyph_render_request &r) { return l < r; }); 132 | 133 | if (i == dedup.end() || *i != r) { 134 | dedup.insert(i, r); 135 | enqueue(r); 136 | } 137 | } 138 | } 139 | 140 | void glyph_renderer_multi::enqueue(glyph_render_request &r) 141 | { 142 | size_t workitem; 143 | do { 144 | workitem = total.load(std::memory_order_relaxed); 145 | if (workitem == capacity) return; 146 | queue[workitem] = r; 147 | } while (!total.compare_exchange_strong(workitem, workitem + 1, 148 | std::memory_order_seq_cst)); 149 | 150 | request.notify_one(); 151 | } 152 | 153 | void glyph_renderer_multi::run() 154 | { 155 | /* if no workers or queue empty, do nothing */ 156 | if (workers.size() == 0 || processed == total) { 157 | return; 158 | } 159 | 160 | /* wake all workers */ 161 | request.notify_all(); 162 | 163 | /* while processed less than queue size, wait for response */ 164 | while (processed < total) { 165 | std::unique_lock lock(mutex); 166 | response.wait(lock); 167 | } 168 | 169 | /* work queue is processed, take lock and clear queue */ 170 | mutex.lock(); 171 | total.store(0, std::memory_order_release); 172 | processing.store(0, std::memory_order_release); 173 | processed.store(0, std::memory_order_release); 174 | dedup.clear(); 175 | queue.clear(); 176 | mutex.unlock(); 177 | } 178 | 179 | void glyph_renderer_multi::shutdown() 180 | { 181 | running = false; 182 | request.notify_all(); 183 | for (size_t i = 0; i < workers.size(); i++) { 184 | workers[i]->thread.join(); 185 | } 186 | workers.clear(); 187 | } -------------------------------------------------------------------------------- /src/multi.h: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #pragma once 4 | 5 | /* 6 | * glyph_render_request 7 | */ 8 | 9 | struct glyph_render_request 10 | { 11 | font_atlas* atlas; 12 | font_face_ft *face; 13 | unsigned glyph; 14 | 15 | const bool operator==(const glyph_render_request &o) const { 16 | return std::tie(atlas, face, glyph) == 17 | std::tie(o.atlas, o.face, o.glyph); 18 | } 19 | const bool operator!=(const glyph_render_request &o) const { 20 | return std::tie(atlas, face, glyph) != 21 | std::tie(o.atlas, o.face, o.glyph); 22 | } 23 | const bool operator<(const glyph_render_request &o) const { 24 | return std::tie(atlas, face, glyph) < 25 | std::tie(o.atlas, o.face, o.glyph); 26 | } 27 | }; 28 | 29 | /* 30 | * glyph_renderer_worker 31 | */ 32 | 33 | struct glyph_renderer_factory 34 | { 35 | virtual ~glyph_renderer_factory() = default; 36 | virtual std::unique_ptr create() = 0; 37 | }; 38 | 39 | template 40 | struct glyph_renderer_factory_impl : glyph_renderer_factory 41 | { 42 | std::unique_ptr create() 43 | { 44 | return std::unique_ptr(new T()); 45 | } 46 | }; 47 | 48 | /* 49 | * glyph_renderer_worker 50 | */ 51 | 52 | struct glyph_renderer_multi; 53 | 54 | struct glyph_renderer_worker 55 | { 56 | static const bool debug = false; 57 | 58 | glyph_renderer_multi* master; 59 | size_t worker_num; 60 | glyph_renderer_factory& renderer_factory; 61 | std::thread thread; 62 | std::map> face_map; 63 | 64 | glyph_renderer_worker(glyph_renderer_multi* master, size_t worker_num, 65 | glyph_renderer_factory& renderer_factory); 66 | 67 | font_face_ft* get_face(font_face_ft *face); 68 | 69 | void mainloop(); 70 | }; 71 | 72 | inline glyph_renderer_worker::glyph_renderer_worker( 73 | glyph_renderer_multi* master, size_t worker_num, 74 | glyph_renderer_factory& renderer_factory) : 75 | master(master), worker_num(worker_num), 76 | renderer_factory(renderer_factory), 77 | thread(&glyph_renderer_worker::mainloop, this) {} 78 | 79 | /* 80 | * glyph_renderer_multi 81 | */ 82 | 83 | struct glyph_renderer_multi 84 | { 85 | static const bool debug = false; 86 | 87 | /* 88 | * renderer specific structure members 89 | * 90 | * variable_size - indicates use of variable size glyph renderer 91 | * manager - font manager used to acquire font atlases 92 | */ 93 | bool variable_size; 94 | font_manager* manager; 95 | 96 | /* 97 | * multithreaded work queue specific structure members 98 | * 99 | * workers - worker threads to process work queue 100 | * running - boolean variable that is cleared to shutdown workers 101 | * queue - storage for work items 102 | * capacity - work item queue capacity 103 | * total - upper bound of items to process, write to start work 104 | * processing - upper bound of items processing, written to dequeue work 105 | * processed - lower bound of items processing, written to finish work 106 | * mutex - lock for condition variable 107 | * request - condition variable waited on by workers to start work 108 | * response - condition variable waited on by master on completed work 109 | */ 110 | std::vector> workers; 111 | std::atomic running; 112 | std::vector dedup; 113 | std::vector queue; 114 | std::atomic capacity; 115 | std::atomic total; 116 | std::atomic processing; 117 | std::atomic processed; 118 | std::mutex mutex; 119 | std::condition_variable request; 120 | std::condition_variable response; 121 | 122 | glyph_renderer_multi(font_manager* manager, 123 | glyph_renderer_factory& renderer_factory, size_t num_threads); 124 | virtual ~glyph_renderer_multi(); 125 | 126 | void add(std::vector &shapes, text_segment *segment); 127 | void enqueue(glyph_render_request &r); 128 | void run(); 129 | void shutdown(); 130 | }; 131 | -------------------------------------------------------------------------------- /src/text.h: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #pragma once 4 | 5 | /* 6 | * Text Attributes 7 | */ 8 | 9 | typedef enum : intptr_t { 10 | text_attr_none, 11 | text_attr_font_name, 12 | text_attr_font_family, 13 | text_attr_font_style, 14 | text_attr_font_weight, 15 | text_attr_font_slope, 16 | text_attr_font_stretch, 17 | text_attr_font_spacing, 18 | text_attr_font_size, 19 | text_attr_color, 20 | text_attr_underline, 21 | text_attr_strike, 22 | } text_attr; 23 | 24 | typedef std::pair tag_pair_t; 25 | typedef std::map tag_map_t; 26 | typedef std::initializer_list tags_initializer_t; 27 | 28 | /* 29 | * Text Part 30 | */ 31 | 32 | struct text_span 33 | { 34 | std::string text; 35 | tag_map_t tags; 36 | 37 | text_span() = default; 38 | text_span(std::string s); 39 | text_span(std::string s, tag_map_t t); 40 | text_span(std::string s, tags_initializer_t l); 41 | 42 | std::string to_string(); 43 | }; 44 | 45 | /* 46 | * Text Container 47 | */ 48 | 49 | struct text_container 50 | { 51 | std::vector parts; 52 | 53 | text_container() = default; 54 | text_container(std::string s); 55 | text_container(std::string s, tag_map_t t); 56 | text_container(std::string s, tags_initializer_t l); 57 | text_container(text_span c); 58 | 59 | void erase(size_t offset, size_t count); 60 | void insert(size_t offset, std::string s); 61 | void insert(size_t offset, text_span c); 62 | void append(std::string s); 63 | void append(text_span c); 64 | void mark(size_t offset, size_t count, std::string attr, std::string val); 65 | void unmark(size_t offset, size_t count, std::string attr); 66 | void coalesce(); 67 | 68 | std::string as_plaintext(); 69 | std::string to_string(); 70 | }; 71 | 72 | /* 73 | * Text Layout 74 | */ 75 | 76 | struct text_layout 77 | { 78 | font_manager_ft* manager; 79 | text_shaper* shaper; 80 | text_renderer_ft* renderer; 81 | 82 | const float font_size_default = 12.0f; 83 | const uint32_t color_default = 0xff000000; 84 | const float baseline_shift_default = 0; 85 | const float tracking_default = 0; 86 | 87 | text_layout(font_manager_ft* manager, 88 | text_shaper* shaper, text_renderer_ft* renderer); 89 | 90 | void style(text_segment &segment, text_span &part); 91 | void layout(std::vector &segments, 92 | text_container &container, int x, int y, int width, int height); 93 | }; 94 | 95 | inline text_layout::text_layout(font_manager_ft* manager, text_shaper* shaper, 96 | text_renderer_ft* renderer) : 97 | manager(manager), shaper(shaper), renderer(renderer) {} 98 | -------------------------------------------------------------------------------- /src/utf8.cc: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #include "utf8.h" 4 | 5 | size_t utf8_codelen(const char* s) 6 | { 7 | if (!s) { 8 | return 0; 9 | } else if ((s[0]&0x80) == 0x0) { 10 | return 1; 11 | } else if ((s[0]&0xF8) == 0xF8) { 12 | return 5; 13 | } else if ((s[0]&0xF0) == 0xF0) { 14 | return 4; 15 | } else if ((s[0]&0xE0) == 0xE0) { 16 | return 3; 17 | } else if ((s[0]&0xC0) == 0xC0) { 18 | return 2; 19 | } else { 20 | return 1; 21 | } 22 | } 23 | 24 | size_t utf8_strlen(const char *s) 25 | { 26 | size_t count = 0; 27 | while (*s) { 28 | size_t c = *s; 29 | s++; 30 | if ((c&0xC0) == 0xC0) { 31 | s++; 32 | if ((c&0xE0) == 0xE0) { 33 | s++; 34 | if ((c&0xF0) == 0xF0) { 35 | s++; 36 | if ((c&0xF8) == 0xF8) { 37 | s++; 38 | } 39 | } 40 | } 41 | } 42 | count++; 43 | } 44 | return count; 45 | } 46 | 47 | enum { 48 | m = 0x3f, n = 0x1f, o = 0xf, p = 0x7, 49 | q = 0xc0, r = 0xe0, t = 0xf0, u = 0xf8, v = 0x80 50 | }; 51 | 52 | uint32_t utf8_to_utf32(const char *s) 53 | { 54 | if (!s) { 55 | return -1; 56 | } else if ((s[0]&v) == 0x0) { 57 | return s[0]; 58 | } else if ((s[0]&u) == u) { 59 | return ((s[0]&p)<<24)|((s[1]&m)<<18)|((s[2]&m)<<12)|((s[3]&m)<<6)|(s[4]&m); 60 | } else if ((s[0]&t) == t) { 61 | return ((s[0]&o)<<18)|((s[1]&m)<<12)|((s[2]&m)<<6)|(s[3]&m); 62 | } else if ((s[0]&r) == r) { 63 | return ((s[0]&n)<<12)|((s[1]&m)<<6)|(s[2]&m); 64 | } else if ((s[0]&q) == q) { 65 | return ((s[0]&m)<<6)|(s[1]&m); 66 | } else { 67 | return -1; 68 | } 69 | } 70 | 71 | utf32_code utf8_to_utf32_code(const char *s) 72 | { 73 | if (!s) { 74 | return { -1, 0 }; 75 | } else if ((s[0]&v) == 0x0) { 76 | return { s[0], 1 }; 77 | } else if ((s[0]&u) == u) { 78 | return { ((s[0]&p)<<24)|((s[1]&m)<<18)|((s[2]&m)<<12)|((s[3]&m)<<6)|(s[4]&m), 5 }; 79 | } else if ((s[0]&t) == t) { 80 | return { ((s[0]&o)<<18)|((s[1]&m)<<12)|((s[2]&m)<<6)|(s[3]&m), 4 }; 81 | } else if ((s[0]&r) == r) { 82 | return { ((s[0]&n)<<12)|((s[1]&m)<<6)|(s[2]&m), 3 }; 83 | } else if ((s[0]&q) == q) { 84 | return { ((s[0]&m)<<6)|(s[1]&m), 2 }; 85 | } else { 86 | return { -1, 1 }; 87 | } 88 | } 89 | 90 | int utf32_to_utf8(char *s, size_t len, uint32_t c) 91 | { 92 | if (c < 0x80 && len >= 2) { 93 | s[0] = c; 94 | s[1] = 0; 95 | return 1; 96 | } else if (c < 0x800 && len >= 3) { 97 | s[0] = q|((c>>6)&0b11111); 98 | s[1] = v|(c&0b111111); 99 | s[2] = 0; 100 | return 2; 101 | } else if (c < 0x10000 && len >= 4) { 102 | s[0] = r|((c>>12)&0b1111); 103 | s[1] = v|((c>>6)&0b111111); 104 | s[2] = v|(c&0b111111); 105 | s[3] = 0; 106 | return 3; 107 | } else if (c < 0x110000 && len >= 5) { 108 | s[0] = t|((c>>18)&0b111); 109 | s[1] = v|((c>>12)&0b111111); 110 | s[2] = v|((c>>6)&0b111111); 111 | s[3] = v|(c&0b111111); 112 | s[4] = 0; 113 | return 4; 114 | } else if (c < 0x110000 && len >= 6) { 115 | s[0] = u|((c>>24)&0b11); 116 | s[1] = v|((c>>18)&0b111111); 117 | s[2] = v|((c>>12)&0b111111); 118 | s[3] = v|((c>>6)&0b111111); 119 | s[4] = v|(c&0b111111); 120 | s[5] = 0; 121 | return 5; 122 | } 123 | return -1; 124 | } 125 | -------------------------------------------------------------------------------- /src/utf8.h: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | size_t utf8_codelen(const char* s); 16 | size_t utf8_strlen(const char *s); 17 | uint32_t utf8_to_utf32(const char *s); 18 | int utf32_to_utf8(char *s, size_t len, uint32_t c); 19 | 20 | struct utf32_code { intptr_t code; intptr_t len; }; 21 | utf32_code utf8_to_utf32_code(const char *s); 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | -------------------------------------------------------------------------------- /src/worker.h: -------------------------------------------------------------------------------- 1 | // See LICENSE for license details. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | /* 14 | * == Overview == 15 | * 16 | * C++ threaded worker pool that executes work-items from a queue, featuring: 17 | * 18 | * - customizable work item 19 | * - customizable worker 20 | * - controllable concurrency 21 | * 22 | * designed to dispatch 'm' irregular sized items to a pool of 'n' threads. 23 | * dispatcher asynchronously passes work-items to worker threads who execute 24 | * work-items with persistent worker instances, then signal completion back 25 | * to the dispatcher which then processes any remaining work-items. 26 | * 27 | * application state for workers is created by a pool worker factory lambda. 28 | * worker threads have one worker object implementing the pool_worker protocol. 29 | * 30 | * pool dispatcher contains the work queue and creates workers and threads 31 | * with mainloops that process work-items one-by-one calling exec on a worker. 32 | * 33 | * == Usage == 34 | * 35 | * user defines an ITEM type and a WORKER type that implements pool_worker: 36 | * 37 | * template struct pool_worker 38 | * { 39 | * virtual ~pool_worker() = default; 40 | * virtual void operator()(ITEM &wi) = 0; 41 | * }; 42 | * 43 | * template struct pool_executor; 44 | * 45 | * == Definitions == 46 | * 47 | * pool_worker - protocol implementing exec(const pool_workitem&). 48 | * pool_executor - dispatches work items to worker thread's workers. 49 | * manages worker threads and a queue of work items. 50 | * pool_worker_thread - composes std:thread and contains worker mainloop. 51 | */ 52 | 53 | template struct pool_worker; 54 | template struct pool_executor; 55 | template struct pool_worker_thread; 56 | 57 | /* 58 | * pool_worker protocol 59 | */ 60 | 61 | template struct pool_worker 62 | { 63 | virtual ~pool_worker() = default; 64 | virtual void operator()(ITEM &wi) = 0; 65 | }; 66 | 67 | /* 68 | * pool_executor 69 | */ 70 | 71 | template 72 | struct pool_executor 73 | { 74 | typedef std::function worker_factory_fn; 75 | 76 | /* 77 | * multithreaded work queue specific structure members 78 | * 79 | * workers - worker threads to process work queue 80 | * running - boolean variable that is cleared to shutdown workers 81 | * queue - storage for work items 82 | * total - upper bound of items to process, write to start work 83 | * processing - upper bound of items processing, written to dequeue work 84 | * processed - lower bound of items processing, written to finish work 85 | * mutex - lock for condition variable 86 | * request - condition variable waited on by workers 87 | * response - condition variable waited on by executor 88 | */ 89 | std::vector>> workers; 90 | std::atomic running; 91 | std::vector queue; 92 | std::atomic total; 93 | std::atomic processing; 94 | std::atomic processed; 95 | std::mutex mutex; 96 | std::condition_variable request; 97 | std::condition_variable response; 98 | 99 | pool_executor(size_t num_threads, size_t queue_size, 100 | const worker_factory_fn &worker_factory = [](){ 101 | return new WORKER(); 102 | }); 103 | virtual ~pool_executor(); 104 | 105 | bool enqueue(ITEM &&item); 106 | bool enqueue(const ITEM &item); 107 | 108 | void run(); 109 | void shutdown(); 110 | }; 111 | 112 | /* 113 | * pool_worker_thread 114 | */ 115 | 116 | template 117 | struct pool_worker_thread 118 | { 119 | typedef std::function worker_factory_fn; 120 | 121 | pool_executor& dispatcher; 122 | const worker_factory_fn worker_factory; 123 | const size_t worker_num; 124 | std::thread thread; 125 | 126 | pool_worker_thread(pool_executor& dispatcher, 127 | const worker_factory_fn &worker_factory, size_t worker_num); 128 | 129 | void mainloop(); 130 | }; 131 | 132 | template 133 | pool_worker_thread::pool_worker_thread( 134 | pool_executor& dispatcher, 135 | const std::function &worker_factory, 136 | size_t worker_num 137 | ) : 138 | dispatcher(dispatcher), 139 | worker_factory(worker_factory), 140 | worker_num(worker_num), 141 | thread(&pool_worker_thread::mainloop, this) 142 | {} 143 | 144 | template 145 | void pool_worker_thread::mainloop() 146 | { 147 | std::unique_ptr> worker(worker_factory()); 148 | 149 | while (dispatcher.running) { 150 | size_t total, processing, workitem, processed; 151 | 152 | /* find out how many items still need processing */ 153 | total = dispatcher.total.load(std::memory_order_acquire); 154 | processing = dispatcher.processing.load(std::memory_order_acquire); 155 | 156 | /* sleep on dispatcher condition if there is no work */ 157 | if (processing >= total) { 158 | std::unique_lock lock(dispatcher.mutex); 159 | dispatcher.request.wait(lock); 160 | continue; 161 | } 162 | 163 | /* dequeue work-item and process it */ 164 | workitem = dispatcher.processing.fetch_add(1, std::memory_order_seq_cst); 165 | (*worker)(dispatcher.queue[workitem]); 166 | processed = dispatcher.processed.fetch_add(1, std::memory_order_seq_cst); 167 | 168 | /* notify dispatcher when last item has been processed */ 169 | total = dispatcher.total.load(std::memory_order_acquire); 170 | if (processed == total - 1) { 171 | dispatcher.response.notify_one(); 172 | } 173 | } 174 | } 175 | 176 | /* 177 | * pool_executor implementation 178 | */ 179 | 180 | template 181 | pool_executor::pool_executor(size_t num_threads, size_t queue_size, 182 | const worker_factory_fn &worker_factory 183 | ) : 184 | workers(), running(true), queue(), total(0), processing(0), processed(0), 185 | mutex(), request(), response() 186 | { 187 | for (size_t i = 0; i < num_threads; i++) { 188 | workers.push_back(std::make_unique> 189 | (*this, worker_factory, i)); 190 | } 191 | queue.resize(queue_size); 192 | } 193 | 194 | template 195 | pool_executor::~pool_executor() 196 | { 197 | shutdown(); 198 | } 199 | 200 | template 201 | bool pool_executor::enqueue(ITEM &&item) 202 | { 203 | return enqueue(item); 204 | } 205 | 206 | template 207 | bool pool_executor::enqueue(const ITEM &item) 208 | { 209 | size_t workitem; 210 | do { 211 | workitem = total.load(std::memory_order_relaxed); 212 | if (workitem == queue.size()) return false; 213 | queue[workitem] = item; 214 | } while (!total.compare_exchange_strong(workitem, workitem + 1, 215 | std::memory_order_seq_cst)); 216 | 217 | request.notify_one(); 218 | return true; 219 | } 220 | 221 | template 222 | void pool_executor::run() 223 | { 224 | /* if no workers or queue empty, do nothing */ 225 | if (workers.size() == 0 || processed == total) { 226 | return; 227 | } 228 | 229 | /* wake all workers */ 230 | request.notify_all(); 231 | 232 | /* while processed less than queue size, wait for response */ 233 | while (processed < total) { 234 | std::unique_lock lock(mutex); 235 | response.wait(lock); 236 | } 237 | 238 | /* work queue is processed, take lock and clear queue */ 239 | mutex.lock(); 240 | total.store(0, std::memory_order_release); 241 | processing.store(0, std::memory_order_release); 242 | processed.store(0, std::memory_order_release); 243 | mutex.unlock(); 244 | } 245 | 246 | template 247 | void pool_executor::shutdown() 248 | { 249 | running = false; 250 | request.notify_all(); 251 | for (size_t i = 0; i < workers.size(); i++) { 252 | workers[i]->thread.join(); 253 | } 254 | workers.clear(); 255 | } -------------------------------------------------------------------------------- /src/ztdbits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * ztdbits.h 5 | * 6 | * This header defines the following functions: 7 | * 8 | * clz(int) - count leading zeros 9 | * ctz(int) - count trialing zeros 10 | * popcnt(int) - population count 11 | * 12 | * This header can be included from C++ and uses template specialization 13 | * 14 | * GCC -O2 -x c++ -std=c++14 15 | * MSVC /O2 /TP /std:c++14 16 | * 17 | * This header can be included from C and uses C11 _Generic selection 18 | * 19 | * GCC -O2 -x c -std=c11 20 | * MSVC /O2 /TC /std:c11 21 | */ 22 | 23 | typedef unsigned int __bits_u32; 24 | typedef signed int __bits_s32; 25 | typedef unsigned long long __bits_u64; 26 | typedef signed long long __bits_s64; 27 | 28 | #if defined (_MSC_VER) 29 | #include 30 | #endif 31 | 32 | #define _clz_defined 1 33 | #define _ctz_defined 2 34 | #define _popcnt_defined 4 35 | 36 | /* 37 | * clz, ctz and popcnt mappings to compiler intrinsics 38 | * 39 | * notes 40 | * 41 | * 1. zero test should be optimized out whem lowering to tzcount and lzcount 42 | * 2. zero test is required for the older bitscan instructions. 43 | * 3. popcount intrinsic is missing on i386. 44 | */ 45 | #if defined (__GNUC__) 46 | static inline unsigned clz_u32(__bits_u32 val) { return val == 0 ? 32 : __builtin_clz(val); } 47 | static inline unsigned clz_u64(__bits_u64 val) { return val == 0 ? 64 : __builtin_clzll(val); } 48 | static inline unsigned ctz_u32(__bits_u32 val) { return val == 0 ? 32 : __builtin_ctz(val); } 49 | static inline unsigned ctz_u64(__bits_u64 val) { return val == 0 ? 64 : __builtin_ctzll(val); } 50 | static inline unsigned popcnt_u32(__bits_u32 val) { return __builtin_popcount(val); } 51 | static inline unsigned popcnt_u64(__bits_u64 val) { return __builtin_popcountll(val); } 52 | #define _bits_defined (_clz_defined | _ctz_defined | _popcnt_defined) 53 | #elif defined (_MSC_VER) && defined (_M_X64) 54 | static inline unsigned clz_u32(__bits_u32 val) { return (int)_lzcnt_u32(val); } 55 | static inline unsigned clz_u64(__bits_u64 val) { return (int)_lzcnt_u64(val); } 56 | static inline unsigned ctz_u32(__bits_u32 val) { return (int)_tzcnt_u32(val); } 57 | static inline unsigned ctz_u64(__bits_u64 val) { return (int)_tzcnt_u64(val); } 58 | static inline unsigned popcnt_u32(__bits_u32 val) { return (int)__popcnt(val); } 59 | static inline unsigned popcnt_u64(__bits_u64 val) { return (int)__popcnt64(val); } 60 | #define _bits_defined (_clz_defined | _ctz_defined | _popcnt_defined) 61 | #elif defined (_MSC_VER) && defined (_M_IX86) 62 | static inline unsigned clz_u32(__bits_u32 val) { unsigned long count; return val == 0 ? 32 : (_BitScanReverse(&count, val) ^ 31); } 63 | static inline unsigned clz_u64(__bits_u64 val) { unsigned long count; return val == 0 ? 64 : (_BitScanReverse64(&count, val) ^ 63); } 64 | static inline unsigned ctz_u32(__bits_u32 val) { unsigned long count; return val == 0 ? 32 :_BitScanForward(&count, val); } 65 | static inline unsigned ctz_u64(__bits_u64 val) { unsigned long count; return val == 0 ? 64 : _BitScanForward64(&count, val); } 66 | /* there is no popcount intrinsic on i386 */ 67 | #define _bits_defined (_clz_defined | _ctz_defined) 68 | #else 69 | #define _bits_defined 0 70 | #endif 71 | 72 | /* 73 | * fallback algorithms from stanford bit twiddling hacks 74 | */ 75 | 76 | #if (_bits_defined & _popcnt_defined) != _popcnt_defined 77 | static inline unsigned popcnt_u32(__bits_u32 val) 78 | { 79 | val = (val & 0x55555555) + ((val >> 1) & 0x55555555); 80 | val = (val & 0x33333333) + ((val >> 2) & 0x33333333); 81 | val = (val & 0x0F0F0F0F) + ((val >> 4) & 0x0F0F0F0F); 82 | val = (val & 0x00FF00FF) + ((val >> 8) & 0x00FF00FF); 83 | val = (val & 0x0000FFFF) + ((val >>16) & 0x0000FFFF); 84 | return (unsigned)val; 85 | } 86 | static inline unsigned popcnt_u64(__bits_u64 val) 87 | { 88 | val = (val & 0x5555555555555555ULL) + ((val >> 1) & 0x5555555555555555ULL); 89 | val = (val & 0x3333333333333333ULL) + ((val >> 2) & 0x3333333333333333ULL); 90 | val = (val & 0x0F0F0F0F0F0F0F0FULL) + ((val >> 4) & 0x0F0F0F0F0F0F0F0FULL); 91 | val = (val & 0x00FF00FF00FF00FFULL) + ((val >> 8) & 0x00FF00FF00FF00FFULL); 92 | val = (val & 0x0000FFFF0000FFFFULL) + ((val >> 16) & 0x0000FFFF0000FFFFULL); 93 | val = (val & 0x00000000FFFFFFFFULL) + ((val >> 32) & 0x00000000FFFFFFFFULL); 94 | return (unsigned)val; 95 | } 96 | #endif 97 | 98 | #if (_bits_defined & _clz_defined) != _clz_defined 99 | static inline unsigned clz_u32(__bits_u32 x) 100 | { 101 | x = x | (x >> 1); 102 | x = x | (x >> 2); 103 | x = x | (x >> 4); 104 | x = x | (x >> 8); 105 | x = x | (x >>16); 106 | return popcnt_u32(~x); 107 | } 108 | 109 | static inline unsigned clz_u64(__bits_u64 x) 110 | { 111 | x = x | (x >> 1); 112 | x = x | (x >> 2); 113 | x = x | (x >> 4); 114 | x = x | (x >> 8); 115 | x = x | (x >>16); 116 | x = x | (x >>32); 117 | return popcnt_u64(~x); 118 | } 119 | #endif 120 | 121 | #if (_bits_defined & _ctz_defined) != _ctz_defined 122 | static inline unsigned ctz_u32(__bits_u32 v) 123 | { 124 | unsigned c = 32; 125 | v &= -(__bits_s32)v; 126 | if (v) c--; 127 | if (v & 0x0000FFFF) c -= 16; 128 | if (v & 0x00FF00FF) c -= 8; 129 | if (v & 0x0F0F0F0F) c -= 4; 130 | if (v & 0x33333333) c -= 2; 131 | if (v & 0x55555555) c -= 1; 132 | return c; 133 | } 134 | 135 | static inline unsigned ctz_u64(__bits_u64 v) 136 | { 137 | unsigned c = 64; 138 | v &= -(__bits_s64)v; 139 | if (v) c--; 140 | if (v & 0x00000000FFFFFFFFULL) c -= 32; 141 | if (v & 0x0000FFFF0000FFFFULL) c -= 16; 142 | if (v & 0x00FF00FF00FF00FFULL) c -= 8; 143 | if (v & 0x0F0F0F0F0F0F0F0FULL) c -= 4; 144 | if (v & 0x3333333333333333ULL) c -= 2; 145 | if (v & 0x5555555555555555ULL) c -= 1; 146 | return c; 147 | } 148 | #endif 149 | 150 | /* 151 | * C11 generics mapping for clz, ctz and popcnt 152 | */ 153 | #if __STDC_VERSION__ >= 201112L 154 | #define clz(X) _Generic((X), \ 155 | unsigned int: clz_u32, signed int: clz_u32, \ 156 | unsigned long long: clz_u64, signed long long: clz_u64)(X) 157 | #define ctz(X) _Generic((X), \ 158 | unsigned int: ctz_u32, signed int: ctz_u32, \ 159 | unsigned long long: ctz_u64, signed long long: ctz_u64)(X) 160 | #define popcnt(X) _Generic((X), \ 161 | unsigned int: ctz_u32, signed int: popcnt_u32, \ 162 | unsigned long long: ctz_u64, signed long long: popcnt_u64)(X) 163 | #endif 164 | 165 | /* 166 | * C++ template specialization for clz, ctz and popcnt 167 | */ 168 | #if defined (__cplusplus) 169 | template inline unsigned clz(T val); 170 | template<> inline unsigned clz(signed int val) { return clz_u32(val); } 171 | template<> inline unsigned clz(unsigned int val) { return clz_u32(val); } 172 | #if defined (_MSC_VER) 173 | template<> inline unsigned clz(signed long val) { return clz_u32(val); } 174 | template<> inline unsigned clz(unsigned long val) { return clz_u32(val); } 175 | #else 176 | template<> inline unsigned clz(signed long val) { return clz_u64(val); } 177 | template<> inline unsigned clz(unsigned long val) { return clz_u64(val); } 178 | #endif 179 | template<> inline unsigned clz(signed long long val) { return clz_u64(val); } 180 | template<> inline unsigned clz(unsigned long long val) { return clz_u64(val); } 181 | 182 | template inline unsigned ctz(T val); 183 | template<> inline unsigned ctz(signed int val) { return ctz_u32(val); } 184 | template<> inline unsigned ctz(unsigned int val) { return ctz_u32(val); } 185 | #if defined (_MSC_VER) 186 | template<> inline unsigned ctz(signed long val) { return ctz_u32(val); } 187 | template<> inline unsigned ctz(unsigned long val) { return ctz_u32(val); } 188 | #else 189 | template<> inline unsigned ctz(signed long val) { return ctz_u64(val); } 190 | template<> inline unsigned ctz(unsigned long val) { return ctz_u64(val); } 191 | #endif 192 | template<> inline unsigned ctz(signed long long val) { return ctz_u64(val); } 193 | template<> inline unsigned ctz(unsigned long long val) { return ctz_u64(val); } 194 | 195 | template inline unsigned popcnt(T val); 196 | template<> inline unsigned popcnt(signed int val) { return popcnt_u32(val); } 197 | template<> inline unsigned popcnt(unsigned int val) { return popcnt_u32(val); } 198 | #if defined (_MSC_VER) 199 | template<> inline unsigned popcnt(signed long val) { return popcnt_u32(val); } 200 | template<> inline unsigned popcnt(unsigned long val) { return popcnt_u32(val); } 201 | #else 202 | template<> inline unsigned popcnt(signed long val) { return popcnt_u64(val); } 203 | template<> inline unsigned popcnt(unsigned long val) { return popcnt_u64(val); } 204 | #endif 205 | template<> inline unsigned popcnt(signed long long val) { return popcnt_u64(val); } 206 | template<> inline unsigned popcnt(unsigned long long val) { return popcnt_u64(val); } 207 | #endif 208 | -------------------------------------------------------------------------------- /src/ztdendian.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * ztdendian.h 5 | * 6 | * This header defines the following endian macros as defined here: 7 | * 8 | * http://austingroupbugs.net/view.php?id=162 9 | * 10 | * BYTE_ORDER this macro shall have a value equal to one 11 | * of the *_ENDIAN macros in this header. 12 | * LITTLE_ENDIAN if BYTE_ORDER == LITTLE_ENDIAN, the host 13 | * byte order is from least significant to 14 | * most significant. 15 | * BIG_ENDIAN if BYTE_ORDER == BIG_ENDIAN, the host byte 16 | * order is from most significant to least 17 | * significant. 18 | * 19 | * This header also defines several byte-swap interfaces, some that 20 | * map directly to the host byte swap intrinsics and some sensitive 21 | * to the host endian representation, performing a swap only if the 22 | * host representation differs from the chosen representation. 23 | * 24 | * Direct byte swapping interfaces: 25 | * 26 | * uint16_t bswap16(uint16_t x); (* swap bytes 16-bit word *) 27 | * uint32_t bswap32(uint32_t x); (* swap bytes 32-bit word *) 28 | * uint64_t bswap64(uint64_t x); (* swap bytes 64-bit word *) 29 | * 30 | * Simplified host endian interfaces: 31 | * 32 | * uint16_t be16(uint16_t x); (* big-endian representation 16-bit word *) 33 | * uint32_t be32(uint32_t x); (* big-endian representation 32-bit word *) 34 | * uint64_t be64(uint64_t x); (* big-endian representation 64-bit word *) 35 | * 36 | * uint16_t le16(uint16_t x); (* little-endian representation 16-bit word *) 37 | * uint32_t le32(uint32_t x); (* little-endian representation 32-bit word *) 38 | * uint64_t le64(uint64_t x); (* little-endian representation 64-bit word *) 39 | * 40 | * BSD host endian interfaces: 41 | * 42 | * uint16_t htobe16(uint16_t x) { return be16(x); } 43 | * uint16_t htole16(uint16_t x) { return le16(x); } 44 | * uint16_t be16toh(uint16_t x) { return be16(x); } 45 | * uint16_t le16toh(uint16_t x) { return le16(x); } 46 | * 47 | * uint32_t htobe32(uint32_t x) { return be32(x); } 48 | * uint32_t htole32(uint32_t x) { return le32(x); } 49 | * uint32_t be32toh(uint32_t x) { return be32(x); } 50 | * uint32_t le32toh(uint32_t x) { return le32(x); } 51 | * 52 | * uint64_t htobe64(uint64_t x) { return be64(x); } 53 | * uint64_t htole64(uint64_t x) { return le64(x); } 54 | * uint64_t be64toh(uint64_t x) { return be64(x); } 55 | * uint64_t le64toh(uint64_t x) { return le64(x); } 56 | */ 57 | 58 | #include 59 | 60 | #ifdef __cplusplus 61 | extern "C" { 62 | #endif 63 | 64 | /* Linux */ 65 | #if defined(__linux__) || defined(__GLIBC__) 66 | #include 67 | #include 68 | #define __ENDIAN_DEFINED 69 | #define __BSWAP_DEFINED 70 | #define __HOSTSWAP_DEFINED 71 | #define _BYTE_ORDER __BYTE_ORDER 72 | #define _LITTLE_ENDIAN __LITTLE_ENDIAN 73 | #define _BIG_ENDIAN __BIG_ENDIAN 74 | #define bswap16(x) bswap_16(x) 75 | #define bswap32(x) bswap_32(x) 76 | #define bswap64(x) bswap_64(x) 77 | #endif /* __linux__ || __GLIBC__ */ 78 | 79 | /* BSD */ 80 | #if defined(__FreeBSD__) || defined(__NetBSD__) || \ 81 | defined(__DragonFly__) || defined(__OpenBSD__) 82 | #include 83 | #define __ENDIAN_DEFINED 84 | #define __BSWAP_DEFINED 85 | #define __HOSTSWAP_DEFINED 86 | #endif /* BSD */ 87 | 88 | /* Apple */ 89 | #if defined(__APPLE__) 90 | #include 91 | #include 92 | #define __ENDIAN_DEFINED 93 | #define __BSWAP_DEFINED 94 | #define _BYTE_ORDER BYTE_ORDER 95 | #define _LITTLE_ENDIAN LITTLE_ENDIAN 96 | #define _BIG_ENDIAN BIG_ENDIAN 97 | #define bswap16(x) OSSwapInt16(x) 98 | #define bswap32(x) OSSwapInt32(x) 99 | #define bswap64(x) OSSwapInt64(x) 100 | #endif /* Apple */ 101 | 102 | /* Windows */ 103 | #if defined(_WIN32) || defined(_MSC_VER) 104 | /* assumes all Microsoft targets are little endian */ 105 | #include 106 | #define _LITTLE_ENDIAN 1234 107 | #define _BIG_ENDIAN 4321 108 | #define _BYTE_ORDER _LITTLE_ENDIAN 109 | #define __ENDIAN_DEFINED 110 | #define __BSWAP_DEFINED 111 | static inline uint16_t bswap16(uint16_t x) { return _byteswap_ushort(x); } 112 | static inline uint32_t bswap32(uint32_t x) { return _byteswap_ulong(x); } 113 | static inline uint64_t bswap64(uint64_t x) { return _byteswap_uint64(x); } 114 | #endif /* Windows */ 115 | 116 | /* OpenCL */ 117 | #if defined (__OPENCL_VERSION__) 118 | #define _LITTLE_ENDIAN 1234 119 | #define _BIG_ENDIAN 4321 120 | #if defined (__ENDIAN_LITTLE__) 121 | #define _BYTE_ORDER _LITTLE_ENDIAN 122 | #else 123 | #define _BYTE_ORDER _BIG_ENDIAN 124 | #endif 125 | #define bswap16(x) as_ushort(as_uchar2(ushort(x)).s1s0) 126 | #define bswap32(x) as_uint(as_uchar4(uint(x)).s3s2s1s0) 127 | #define bswap64(x) as_ulong(as_uchar8(ulong(x)).s7s6s5s4s3s2s1s0) 128 | #define __ENDIAN_DEFINED 129 | #define __BSWAP_DEFINED 130 | #endif 131 | 132 | /* For everything else, use the compiler's predefined endian macros */ 133 | #if !defined (__ENDIAN_DEFINED) && defined (__BYTE_ORDER__) && \ 134 | defined (__ORDER_LITTLE_ENDIAN__) && defined (__ORDER_BIG_ENDIAN__) 135 | #define __ENDIAN_DEFINED 136 | #define _BYTE_ORDER __BYTE_ORDER__ 137 | #define _LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ 138 | #define _BIG_ENDIAN __ORDER_BIG_ENDIAN__ 139 | #endif 140 | 141 | /* No endian macros found */ 142 | #ifndef __ENDIAN_DEFINED 143 | #error Could not determine CPU byte order 144 | #endif 145 | 146 | /* POSIX - http://austingroupbugs.net/view.php?id=162 */ 147 | #ifndef BYTE_ORDER 148 | #define BYTE_ORDER _BYTE_ORDER 149 | #endif 150 | #ifndef LITTLE_ENDIAN 151 | #define LITTLE_ENDIAN _LITTLE_ENDIAN 152 | #endif 153 | #ifndef BIG_ENDIAN 154 | #define BIG_ENDIAN _BIG_ENDIAN 155 | #endif 156 | 157 | /* 158 | * Natural to foreign endian helpers defined using bswap 159 | * 160 | * MSC can't lift byte swap expressions efficiently so we 161 | * define host integer swaps using explicit byte swapping. 162 | */ 163 | 164 | /* helps to have these function for symmetry */ 165 | static inline uint8_t le8(uint8_t x) { return x; } 166 | static inline uint8_t be8(uint8_t x) { return x; } 167 | 168 | #if defined(__BSWAP_DEFINED) 169 | #if _BYTE_ORDER == _BIG_ENDIAN 170 | static inline uint16_t be16(uint16_t x) { return x; } 171 | static inline uint32_t be32(uint32_t x) { return x; } 172 | static inline uint64_t be64(uint64_t x) { return x; } 173 | static inline uint16_t le16(uint16_t x) { return bswap16(x); } 174 | static inline uint32_t le32(uint32_t x) { return bswap32(x); } 175 | static inline uint64_t le64(uint64_t x) { return bswap64(x); } 176 | #endif 177 | #if _BYTE_ORDER == _LITTLE_ENDIAN 178 | static inline uint16_t be16(uint16_t x) { return bswap16(x); } 179 | static inline uint32_t be32(uint32_t x) { return bswap32(x); } 180 | static inline uint64_t be64(uint64_t x) { return bswap64(x); } 181 | static inline uint16_t le16(uint16_t x) { return x; } 182 | static inline uint32_t le32(uint32_t x) { return x; } 183 | static inline uint64_t le64(uint64_t x) { return x; } 184 | #endif 185 | 186 | #else 187 | #define __BSWAP_DEFINED 188 | 189 | /* 190 | * Natural to foreign endian helpers using type punning 191 | * 192 | * Recent Clang and GCC lift these expressions to bswap 193 | * instructions. This makes baremetal code easier. 194 | */ 195 | 196 | static inline uint16_t be16(uint16_t x) 197 | { 198 | union { uint8_t a[2]; uint16_t b; } y = { 199 | .a = { (uint8_t)(x >> 8), (uint8_t)(x) } 200 | }; 201 | return y.b; 202 | } 203 | 204 | static inline uint16_t le16(uint16_t x) 205 | { 206 | union { uint8_t a[2]; uint16_t b; } y = { 207 | .a = { (uint8_t)(x), (uint8_t)(x >> 8) } 208 | }; 209 | return y.b; 210 | } 211 | 212 | static inline uint32_t be32(uint32_t x) 213 | { 214 | union { uint8_t a[4]; uint32_t b; } y = { 215 | .a = { (uint8_t)(x >> 24), (uint8_t)(x >> 16), 216 | (uint8_t)(x >> 8), (uint8_t)(x) } 217 | }; 218 | return y.b; 219 | } 220 | 221 | static inline uint32_t le32(uint32_t x) 222 | { 223 | union { uint8_t a[4]; uint32_t b; } y = { 224 | .a = { (uint8_t)(x), (uint8_t)(x >> 8), 225 | (uint8_t)(x >> 16), (uint8_t)(x >> 24) } 226 | }; 227 | return y.b; 228 | } 229 | 230 | static inline uint64_t be64(uint64_t x) 231 | { 232 | union { uint8_t a[8]; uint64_t b; } y = { 233 | .a = { (uint8_t)(x >> 56), (uint8_t)(x >> 48), 234 | (uint8_t)(x >> 40), (uint8_t)(x >> 32), 235 | (uint8_t)(x >> 24), (uint8_t)(x >> 16), 236 | (uint8_t)(x >> 8), (uint8_t)(x) } 237 | }; 238 | return y.b; 239 | } 240 | 241 | static inline uint64_t le64(uint64_t x) 242 | { 243 | union { uint8_t a[8]; uint64_t b; } y = { 244 | .a = { (uint8_t)(x), (uint8_t)(x >> 8), 245 | (uint8_t)(x >> 16), (uint8_t)(x >> 24), 246 | (uint8_t)(x >> 32), (uint8_t)(x >> 40), 247 | (uint8_t)(x >> 48), (uint8_t)(x >> 56) } 248 | }; 249 | return y.b; 250 | } 251 | 252 | /* 253 | * Define byte swaps using the natural endian helpers 254 | * 255 | * This method relies on the compiler lifting byte swaps. 256 | */ 257 | #if _BYTE_ORDER == _BIG_ENDIAN 258 | uint16_t bswap16(uint16_t x) { return le16(x); } 259 | uint32_t bswap32(uint32_t x) { return le32(x); } 260 | uint64_t bswap64(uint64_t x) { return le64(x); } 261 | #endif 262 | 263 | #if _BYTE_ORDER == _LITTLE_ENDIAN 264 | uint16_t bswap16(uint16_t x) { return be16(x); } 265 | uint32_t bswap32(uint32_t x) { return be32(x); } 266 | uint64_t bswap64(uint64_t x) { return be64(x); } 267 | #endif 268 | #endif 269 | 270 | /* 271 | * BSD host integer interfaces 272 | */ 273 | 274 | #ifndef __HOSTSWAP_DEFINED 275 | static inline uint16_t htobe16(uint16_t x) { return be16(x); } 276 | static inline uint16_t htole16(uint16_t x) { return le16(x); } 277 | static inline uint16_t be16toh(uint16_t x) { return be16(x); } 278 | static inline uint16_t le16toh(uint16_t x) { return le16(x); } 279 | 280 | static inline uint32_t htobe32(uint32_t x) { return be32(x); } 281 | static inline uint32_t htole32(uint32_t x) { return le32(x); } 282 | static inline uint32_t be32toh(uint32_t x) { return be32(x); } 283 | static inline uint32_t le32toh(uint32_t x) { return le32(x); } 284 | 285 | static inline uint64_t htobe64(uint64_t x) { return be64(x); } 286 | static inline uint64_t htole64(uint64_t x) { return le64(x); } 287 | static inline uint64_t be64toh(uint64_t x) { return be64(x); } 288 | static inline uint64_t le64toh(uint64_t x) { return le64(x); } 289 | #endif 290 | 291 | #ifdef __cplusplus 292 | } 293 | #endif 294 | -------------------------------------------------------------------------------- /tests/test0001.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "binpack.h" 9 | 10 | void test(bin_rect r1, bin_rect r2) 11 | { 12 | auto l1 = r1.intersect_subset(r2); 13 | auto l2 = r1.disjoint_subset(r2); 14 | printf("A = (%d,%d - %d,%d), B = (%d,%d - %d,%d):\n", 15 | r1.a.x, r1.a.y, r1.b.x, r1.b.y, r2.a.x, r2.a.y, r2.b.x, r2.b.y); 16 | printf("\tA ∩ B:\n"); 17 | for (auto r : l1) { 18 | printf("\t\t(%d,%d - %d,%d)\n", r.a.x, r.a.y, r.b.x, r.b.y); 19 | } 20 | printf("\tA - A ∩ B:\n"); 21 | for (auto r : l2) { 22 | printf("\t\t(%d,%d - %d,%d)\n", r.a.x, r.a.y, r.b.x, r.b.y); 23 | } 24 | } 25 | 26 | void run_tests() 27 | { 28 | /* 29 | * Outside cases: 30 | * 31 | * o ooo 32 | * /---\ /---\o /---\ /---\ 33 | * | | | |o | | | | 34 | * | | | | | | | | 35 | * o| | | | | | | | 36 | * o\---/ \---/ \---/ \---/ 37 | * o ooo 38 | */ 39 | 40 | /* 41 | * Fully inside case (0-axis crossing): 42 | * 43 | * 44 | * /---\ 45 | * | | 46 | * | o | 47 | * | | 48 | * \---/ 49 | * 50 | */ 51 | test(bin_rect({1,1},{4,4}), bin_rect({2,2},{3,3})); 52 | 53 | /* 54 | * Overlap cases (1-axis crossing): 55 | * 56 | * o 57 | * /---\ /---\ /-o-\ /---\ 58 | * | | | | | o | | | 59 | * | | ooo | | | | ooo 60 | * | o | | | | | | | 61 | * \ o-/ \---/ \---/ \---/ 62 | * o 63 | */ 64 | test(bin_rect({2,2},{5,5}), bin_rect({3,3},{4,6})); 65 | test(bin_rect({2,2},{5,5}), bin_rect({1,3},{3,4})); 66 | test(bin_rect({2,2},{5,5}), bin_rect({3,1},{4,4})); 67 | test(bin_rect({2,2},{5,5}), bin_rect({3,3},{6,4})); 68 | 69 | /* 70 | * Overlap cases (2-axis crossing): 71 | * 72 | * ooo ooo 73 | * /---\ ooo--\ /--ooo /---\ 74 | * | | ooo | | ooo | | 75 | * | | | | | | | | 76 | * ooo | | | | | | ooo 77 | * ooo--/ \---/ \---/ \--ooo 78 | * ooo ooo 79 | */ 80 | test(bin_rect({2,2},{4,4}), bin_rect({1,3},{3,5})); 81 | test(bin_rect({2,2},{4,4}), bin_rect({1,1},{3,3})); 82 | test(bin_rect({2,2},{4,4}), bin_rect({3,1},{5,3})); 83 | test(bin_rect({2,2},{4,4}), bin_rect({3,3},{5,5})); 84 | 85 | /* 86 | * Overlap cases (3-axis crossing): 87 | * 88 | * ooo ooooooo ooo 89 | * ooo--\ ooooooo /--ooo /---\ 90 | * ooo | ooooooo | ooo | | 91 | * ooo | | | | ooo | | 92 | * ooo | | | | ooo ooooooo 93 | * ooo--/ \---/ \--ooo ooooooo 94 | * ooo ooo ooooooo 95 | */ 96 | 97 | /* 98 | * Fully surrounded case (4-axis crossing): 99 | * 100 | * ooooooo 101 | * o/---\o 102 | * o|ooo|o 103 | * o|ooo|o 104 | * o|ooo|o 105 | * o\---/o 106 | * ooooooo 107 | */ 108 | test(bin_rect({2,2},{3,3}), bin_rect({1,1},{4,4})); 109 | 110 | } 111 | 112 | int main() 113 | { 114 | run_tests(); 115 | } 116 | -------------------------------------------------------------------------------- /tests/test0002.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "binpack.h" 9 | 10 | int main() 11 | { 12 | bin_packer p(bin_point(10,10)); 13 | assert(p.find_region(1,bin_point(1,1)).first); 14 | assert(p.find_region(2,bin_point(1,1)).first); 15 | assert(p.find_region(3,bin_point(1,1)).first); 16 | assert(p.find_region(4,bin_point(1,1)).first); 17 | assert(p.find_region(5,bin_point(2,2)).first); 18 | assert(p.find_region(6,bin_point(2,2)).first); 19 | assert(p.find_region(7,bin_point(3,1)).first); 20 | assert(p.find_region(8,bin_point(3,1)).first); 21 | assert(p.find_region(9,bin_point(3,1)).first); 22 | p.dump(); 23 | } 24 | -------------------------------------------------------------------------------- /tests/test0003.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "binpack.h" 13 | 14 | void stats(bin_packer &bp) 15 | { 16 | int alloc_area = 0; 17 | for (auto i : bp.alloc_map) { 18 | alloc_area += i.second.area(); 19 | } 20 | float alloc_percent = ((float)alloc_area/(float)bp.total.area()) * 100.0f; 21 | printf("------------------------------\n"); 22 | printf("free list node count = %zu\n", bp.free_list.size()); 23 | printf("alloc map node count = %zu\n", bp.alloc_map.size()); 24 | printf("bin dimensions = %d,%d\n", bp.total.width(), bp.total.height()); 25 | printf("bin total area = %d\n", bp.total.area()); 26 | printf("bin allocated area = %d\n", alloc_area); 27 | printf("bin utilization = %4.1f%%\n", alloc_percent); 28 | } 29 | 30 | int r(int b, int v) 31 | { 32 | return b + (int)floorf(((float)rand()/(float)RAND_MAX)*(float)(v)); 33 | } 34 | 35 | void run_test(int w, int h, int b, int v) 36 | { 37 | bin_packer bp(bin_point(w,h)); 38 | 39 | size_t i = 1; 40 | srand(1); 41 | const auto t1 = std::chrono::high_resolution_clock::now(); 42 | for (;;) { 43 | auto l = bp.find_region(i++,bin_point(r(b,v),r(b,v))); 44 | if (!l.first) break; 45 | }; 46 | const auto t2 = std::chrono::high_resolution_clock::now(); 47 | float runtime = (float)std::chrono::duration_cast(t2 - t1).count() / 1.e9f; 48 | bp.dump(); 49 | bp.verify(); 50 | stats(bp); 51 | printf("runtime = %f seconds\n", runtime); 52 | } 53 | 54 | int main() 55 | { 56 | //run_test(512,512,1,31); 57 | //run_test(1024,1024,1,31); 58 | run_test(1024,1024,16,16); 59 | } 60 | -------------------------------------------------------------------------------- /tests/test0004.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "binpack.h" 22 | #include "utf8.h" 23 | #include "image.h" 24 | #include "draw.h" 25 | #include "font.h" 26 | #include "glyph.h" 27 | #include "text.h" 28 | 29 | using namespace std::chrono; 30 | 31 | const char* test_str_1 = "the quick brown fox jumps over the lazy dog"; 32 | static const char* text_lang = "en"; 33 | size_t iterations = 100000; 34 | 35 | int main() 36 | { 37 | font_manager_ft manager; 38 | auto face = manager.findFontByPath("fonts/Roboto-Regular.ttf"); 39 | 40 | std::vector shapes; 41 | draw_list batch; 42 | 43 | text_shaper_hb shaper; 44 | text_renderer_ft renderer(&manager); 45 | text_segment segment(test_str_1, text_lang, face, 46 | 48 * 64, 0, 0, 0xffffffff); 47 | 48 | /* shape (cold) */ 49 | const auto t1 = high_resolution_clock::now(); 50 | shaper.shape(shapes, segment); 51 | const auto t2 = high_resolution_clock::now(); 52 | 53 | shapes.clear(); 54 | 55 | /* shape (hot) */ 56 | const auto t3 = high_resolution_clock::now(); 57 | shaper.shape(shapes, segment); 58 | const auto t4 = high_resolution_clock::now(); 59 | 60 | /* render (cold) */ 61 | const auto t5 = high_resolution_clock::now(); 62 | renderer.render(batch, shapes, segment); 63 | const auto t6 = high_resolution_clock::now(); 64 | 65 | draw_list_clear(batch); 66 | 67 | /* render (hot) */ 68 | const auto t7 = high_resolution_clock::now(); 69 | renderer.render(batch, shapes, segment); 70 | const auto t8 = high_resolution_clock::now(); 71 | 72 | /* shape (loop) */ 73 | const auto t9 = high_resolution_clock::now(); 74 | for (size_t i = 0; i < iterations; i++) { 75 | shapes.clear(); 76 | shaper.shape(shapes, segment); 77 | } 78 | const auto t10 = high_resolution_clock::now(); 79 | 80 | /* render (loop) */ 81 | const auto t11 = high_resolution_clock::now(); 82 | for (size_t i = 0; i < iterations; i++) { 83 | draw_list_clear(batch); 84 | renderer.render(batch, shapes, segment); 85 | } 86 | const auto t12 = high_resolution_clock::now(); 87 | 88 | float r1 = (float)duration_cast(t2 - t1).count() / 1e3; 89 | float r2 = (float)duration_cast(t4 - t3).count() / 1e3; 90 | float r3 = (float)duration_cast(t6 - t5).count() / 1e3; 91 | float r4 = (float)duration_cast(t8 - t7).count() / 1e3; 92 | float r5 = (float)duration_cast(t10 - t9).count() / 1e3; 93 | float r6 = (float)duration_cast(t12 - t11).count() / 1e3; 94 | 95 | printf("shape (cold) = %12.3f microseconds\n", r1); 96 | printf("shape (hot) = %12.3f microseconds\n", r2); 97 | printf("render (cold) = %12.3f microseconds\n", r3); 98 | printf("render (hot) = %12.3f microseconds\n", r4); 99 | printf("shape time (per glyph) = %12.3f microseconds\n", r5/(iterations*strlen(test_str_1))); 100 | printf("render time (per glyph) = %12.3f microseconds\n", r6/(iterations*strlen(test_str_1))); 101 | } -------------------------------------------------------------------------------- /tests/test0005.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "binpack.h" 21 | #include "utf8.h" 22 | #include "image.h" 23 | #include "draw.h" 24 | #include "font.h" 25 | #include "glyph.h" 26 | #include "text.h" 27 | 28 | void tr(text_container t, size_t offset, size_t count) 29 | { 30 | static int test_count = 0; 31 | 32 | std::string before = t.as_plaintext(); 33 | t.erase(offset, count); 34 | std::string after = t.as_plaintext(); 35 | bool pass = (after == before.erase(offset, count)); 36 | printf("test-%d before=%-20s after=%-20s : %s (%zu, %zu)\n", 37 | test_count++, before.c_str(), after.c_str(), 38 | pass ? "PASS" : "FAIL", offset, count); 39 | } 40 | 41 | void t1() 42 | { 43 | text_container t; 44 | t.append(text_span("0124")); 45 | t.append(text_span("5678")); 46 | t.append(text_span("9abc")); 47 | tr(t, 0, 12); 48 | tr(t, 2, 2); 49 | tr(t, 2, 4); 50 | tr(t, 2, 6); 51 | tr(t, 2, 8); 52 | tr(t, 8, 2); 53 | tr(t, 8, 4); 54 | tr(t, 1, 2); 55 | tr(t, 10, 2); 56 | } 57 | 58 | void ti(text_container t, size_t offset, std::string s) 59 | { 60 | static int test_count = 0; 61 | 62 | std::string before = t.as_plaintext(); 63 | t.insert(offset, s); 64 | std::string after = t.as_plaintext(); 65 | bool pass = (after == before.insert(offset, s)); 66 | printf("test-%d before=%-20s after=%-20s : %s (%zu, %s)\n", 67 | test_count++, before.c_str(), after.c_str(), 68 | pass ? "PASS" : "FAIL", offset, s.c_str()); 69 | } 70 | 71 | void t2() 72 | { 73 | text_container t; 74 | t.append(text_span("01")); 75 | t.append(text_span("23")); 76 | ti(t, 0, "_"); 77 | ti(t, 1, "_"); 78 | ti(t, 2, "_"); 79 | ti(t, 3, "_"); 80 | ti(t, 4, "_"); 81 | } 82 | 83 | void ti(text_container t, size_t offset, text_span s) 84 | { 85 | static int test_count = 0; 86 | 87 | std::string before = t.as_plaintext(); 88 | t.insert(offset, s); 89 | std::string after = t.as_plaintext(); 90 | bool pass = (after == before.insert(offset, s.text)); 91 | printf("test-%d before=%-20s after=%-20s : %s (%zu, %s)\n", 92 | test_count++, before.c_str(), after.c_str(), 93 | pass ? "PASS" : "FAIL", offset, s.text.c_str()); 94 | } 95 | 96 | void t3() 97 | { 98 | text_container t; 99 | t.append(text_span("01")); 100 | t.append(text_span("23")); 101 | ti(t, 0, text_span("_")); 102 | ti(t, 1, text_span("_")); 103 | ti(t, 2, text_span("_")); 104 | ti(t, 3, text_span("_")); 105 | ti(t, 4, text_span("_")); 106 | } 107 | 108 | void t4() 109 | { 110 | text_container t; 111 | t.append(text_span("01", {{ "0", "0" }} )); 112 | t.append(text_span("23", {{ "1", "1" }} )); 113 | ti(t, 0, text_span("_")); 114 | ti(t, 1, text_span("_")); 115 | ti(t, 2, text_span("_")); 116 | ti(t, 3, text_span("_")); 117 | ti(t, 4, text_span("_")); 118 | } 119 | 120 | void tm(text_container t, size_t offset, size_t count, std::string attr, std::string value) 121 | { 122 | static int test_count = 0; 123 | 124 | printf("before : %s\n", t.to_string().c_str()); 125 | std::string before = t.as_plaintext(); 126 | t.mark(offset, count, attr, value); 127 | printf("after : %s\n", t.to_string().c_str()); 128 | std::string after = t.as_plaintext(); 129 | bool pass = (before == after); 130 | printf("test-%d before=%-20s after=%-20s : %s add_attr(%zu, %zu, %s=%s)\n", 131 | test_count++, before.c_str(), after.c_str(), 132 | pass ? "PASS" : "FAIL", offset, count, attr.c_str(), value.c_str()); 133 | } 134 | 135 | 136 | void t5() 137 | { 138 | text_container t; 139 | t.append(text_span("01")); 140 | t.append(text_span("23", {{ "1", "1" }} )); 141 | tm(t, 0, 1, "3", "3"); 142 | tm(t, 0, 2, "3", "3"); 143 | tm(t, 0, 3, "3", "3"); 144 | tm(t, 0, 4, "3", "3"); 145 | tm(t, 1, 1, "3", "3"); 146 | tm(t, 1, 2, "3", "3"); 147 | tm(t, 1, 3, "3", "3"); 148 | tm(t, 2, 1, "3", "3"); 149 | tm(t, 2, 2, "3", "3"); 150 | } 151 | 152 | void tu(text_container t, size_t offset, size_t count, std::string attr) 153 | { 154 | static int test_count = 0; 155 | 156 | printf("before : %s\n", t.to_string().c_str()); 157 | std::string before = t.as_plaintext(); 158 | t.unmark(offset, count, attr); 159 | printf("after : %s\n", t.to_string().c_str()); 160 | std::string after = t.as_plaintext(); 161 | bool pass = (before == after); 162 | printf("test-%d before=%-20s after=%-20s : %s del_attr(%zu, %zu, %s)\n", 163 | test_count++, before.c_str(), after.c_str(), 164 | pass ? "PASS" : "FAIL", offset, count, attr.c_str()); 165 | } 166 | 167 | void t6() 168 | { 169 | text_container t; 170 | t.append(text_span("01", {{ "1", "1" }} )); 171 | t.append(text_span("23", {{ "1", "1" }} )); 172 | tu(t, 0, 1, "1"); 173 | tu(t, 0, 2, "1"); 174 | tu(t, 0, 3, "1"); 175 | tu(t, 0, 4, "1"); 176 | tu(t, 1, 1, "1"); 177 | tu(t, 1, 2, "1"); 178 | tu(t, 1, 3, "1"); 179 | tu(t, 2, 1, "1"); 180 | tu(t, 2, 2, "1"); 181 | } 182 | 183 | int main() 184 | { 185 | t1(); 186 | t2(); 187 | t3(); 188 | t4(); 189 | t5(); 190 | t6(); 191 | } -------------------------------------------------------------------------------- /tests/test0006.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "binpack.h" 21 | #include "image.h" 22 | #include "draw.h" 23 | #include "font.h" 24 | #include "glyph.h" 25 | #include "text.h" 26 | 27 | std::string font_family_default = font_family_any; 28 | std::string font_style_default = font_style_any; 29 | font_weight font_weight_default = font_weight_regular; 30 | font_slope font_slope_default = font_slope_any; 31 | font_stretch font_stretch_default = font_stretch_any; 32 | font_spacing font_spacing_default = font_spacing_any; 33 | int font_size_default = 12; 34 | 35 | const char* text_attributes[] = { 36 | "none", 37 | "font-name", 38 | "font-family", 39 | "font-style", 40 | "font-weight", 41 | "font-slope", 42 | "font-stretch", 43 | "font-spacing", 44 | "font-size", 45 | "text-color", 46 | "underline", 47 | "strike", 48 | }; 49 | 50 | static font_manager_ft manager; 51 | 52 | const char* test_str_1 = "the quick brown fox jumps over the lazy dog"; 53 | static const char* text_lang = "en"; 54 | 55 | void t1() 56 | { 57 | font_face *f; 58 | 59 | f = manager.findFontByFamily("Roboto", font_style_normal); 60 | if (f) { 61 | printf("byFamily: %s\n", f->getFontData().toString().c_str()); 62 | } 63 | 64 | f = manager.findFontByName("Roboto-Light"); 65 | if (f) { 66 | printf("byName: %s\n", f->getFontData().toString().c_str()); 67 | } 68 | 69 | font_data fontData; 70 | fontData.familyName = font_family_any; 71 | fontData.styleName = font_style_any; 72 | fontData.fontWeight = font_weight_any; 73 | fontData.fontSlope = font_slope_any; 74 | fontData.fontStretch = font_stretch_any; 75 | fontData.fontSpacing = font_spacing_any; 76 | f = manager.findFontByData(fontData); 77 | if (f) { 78 | printf("byData: %s\n", f->getFontData().toString().c_str()); 79 | } 80 | } 81 | 82 | void t2() 83 | { 84 | auto face = manager.findFontByPath("fonts/Roboto-Regular.ttf"); 85 | 86 | std::vector segments; 87 | std::vector shapes; 88 | std::vector vertices; 89 | std::vector indices; 90 | 91 | text_shaper_hb shaper; 92 | text_renderer_ft renderer(&manager); 93 | text_segment segment(test_str_1, text_lang, face, 94 | 48 * 64, 0, 0, 0xffffffff); 95 | text_layout layout(&manager, &shaper, &renderer); 96 | 97 | text_container c; 98 | c.append(text_span("regular ", {{ "font-style", "Regular" }})); 99 | c.append(text_span("italic ", {{ "font-style", "Italic" }} )); 100 | c.append(text_span("bold ", {{ "font-weight", "bold" }} )); 101 | 102 | layout.layout(segments, c, 10, 10, 300, 500); 103 | 104 | for (auto &seg : segments) { 105 | if (seg.face) { 106 | printf("x: %f, y: %f, font-name: %s, font-size: %d, text: %s\n", 107 | seg.x, seg.y, seg.face->name.c_str(), seg.font_size, 108 | seg.text.c_str()); 109 | } 110 | } 111 | } 112 | 113 | int main() 114 | { 115 | manager.scanFontDir("fonts"); 116 | t1(); 117 | t2(); 118 | } -------------------------------------------------------------------------------- /tests/test0007.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "color.h" 10 | 11 | void p(color c) 12 | { 13 | printf("color=(%f, %f, %f, %f) rgba32=0x%08x\n", 14 | c.r, c.g, c.b, c.a, c.rgba32()); 15 | } 16 | 17 | void t1() 18 | { 19 | color c("#808080ff"); 20 | p(c); 21 | assert(c.r <= 0.51f); 22 | assert(c.g <= 0.51f); 23 | assert(c.b <= 0.51f); 24 | assert(c.a == 1.0f); 25 | assert(c.rgba32() == 0xff808080); 26 | } 27 | 28 | int main() 29 | { 30 | t1(); 31 | } -------------------------------------------------------------------------------- /tests/test0008.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "binpack.h" 18 | #include "utf8.h" 19 | #include "image.h" 20 | #include "draw.h" 21 | #include "font.h" 22 | #include "glyph.h" 23 | #include "logger.h" 24 | #include "file.h" 25 | 26 | #include "core/arithmetics.hpp" 27 | #include "core/Vector2.h" 28 | #include "core/Scanline.h" 29 | #include "core/Shape.h" 30 | #include "core/BitmapRef.hpp" 31 | #include "core/Bitmap.h" 32 | #include "core/pixel-conversion.hpp" 33 | #include "core/edge-coloring.h" 34 | #include "core/render-sdf.h" 35 | #include "core/rasterization.h" 36 | #include "core/estimate-sdf-error.h" 37 | #include "core/save-bmp.h" 38 | #include "core/save-tiff.h" 39 | #include "core/shape-description.h" 40 | #include "ext/save-png.h" 41 | #include "ext/import-svg.h" 42 | #include "ext/import-font.h" 43 | #include "msdfgen.h" 44 | 45 | #include "core/edge-selectors.h" 46 | #include "core/contour-combiners.h" 47 | 48 | #include 49 | #include FT_FREETYPE_H 50 | #include FT_MODULE_H 51 | #include FT_GLYPH_H 52 | #include FT_OUTLINE_H 53 | 54 | typedef unsigned uint; 55 | 56 | static const char *font_file = "fonts/Roboto-Regular.ttf"; 57 | 58 | static const char* shades[4][4] = { 59 | { " ", "▗", "▖", "▄" }, 60 | { "▝", "▐", "▞", "▟" }, 61 | { "▘", "▚", "▌", "▙" }, 62 | { "▀", "▜", "▛", "█" }, 63 | }; 64 | 65 | static std::string ansi_color(int r, int g, int b) 66 | { 67 | char buf[32]; 68 | snprintf(buf, sizeof(buf), "\x1B[38;2;%u;%u;%um", r, g, b); 69 | return std::string(buf); 70 | } 71 | 72 | static int rupeven(int n) { return (n + 1) & ~1; } 73 | 74 | static void render_block(msdfgen::Bitmap &bitmap, int w, int h) 75 | { 76 | w = rupeven(w), h = rupeven(h); 77 | std::vector v; 78 | for (int y = 0; y < h; y += 2) { 79 | std::string o; 80 | for (int x = 0; x < w; x += 2) { 81 | int c[2][2][3] = {0}, g[2][2] = {0}, s[3] = {0}, b[2][2] = {0}; 82 | for (int u = 0; u < 2; u++) { 83 | for (int v = 0; v < 2; v++) { 84 | for (int w = 0; w < 3; w++) { 85 | int px = msdfgen::pixelFloatToByte(bitmap(x+u,h-y-v-1)[w]); 86 | c[u][v][w] = px; 87 | g[v][u] += c[u][v][w]; 88 | s[w] += c[u][v][w]; 89 | } 90 | b[v][u] = (g[v][u] / 3) > 64; 91 | } 92 | } 93 | o.append(ansi_color(s[0]>>2,s[1]>>2,s[2]>>2)); 94 | o.append(shades[b[0][0]<<1|b[0][1]][b[1][0]<<1|b[1][1]]); 95 | } 96 | printf("%s\n", o.c_str()); 97 | } 98 | printf("%s\n", ansi_color(255,255,255).c_str()); 99 | } 100 | 101 | static void copy_bitmap(image_ptr img, msdfgen::Bitmap &bitmap) 102 | { 103 | for (int y = 0; y < img->width; y++) { 104 | uint8_t *row = img->getData() + y * img->width * 4; 105 | for (int x = 0; x < img->height; x++) { 106 | uint8_t *col = row + x * 4; 107 | for (int w = 0; w < 3; w++) { 108 | col[w] = msdfgen::pixelFloatToByte(bitmap(x,y)[w]); 109 | } 110 | col[3] = 0xff; 111 | } 112 | } 113 | } 114 | 115 | int main(int argc, char **argv) 116 | { 117 | int font_id = 0; 118 | const char *file = nullptr; 119 | bool console = false; 120 | 121 | int dpi = 72; 122 | int width = 64; 123 | int height = 64; 124 | unsigned codepoint = 'a'; 125 | 126 | bool overlapSupport = true; 127 | bool scanlinePass = true; 128 | double range = 1; 129 | double angleThreshold = 3; 130 | double edgeThreshold = 1.001; 131 | double glyphAdvance = 0; 132 | uint coloringSeed = 0; 133 | 134 | msdfgen::Shape shape; 135 | msdfgen::Bitmap msdf(width, height); 136 | msdfgen::Vector2 translate, scale = { 3, 3 }; 137 | 138 | msdfgen::FreetypeHandle *ft = nullptr; 139 | msdfgen::FontHandle *font = nullptr; 140 | 141 | /* 142 | * parse command line arguments 143 | */ 144 | 145 | if (! ((argc == 3 && (strcmp(argv[1], "-file") == 0 && (file = argv[2]))) || 146 | (argc == 2 && (console = strcmp(argv[1], "-console") == 0))) ) { 147 | fprintf(stderr, "usage: %s [-console|-file ]\n", argv[0]); 148 | exit(1); 149 | } 150 | 151 | /* 152 | * load font 153 | */ 154 | 155 | if (!(ft = msdfgen::initializeFreetype())) { 156 | fprintf(stderr, "error: initializeFreetype failed\n"); 157 | exit(1); 158 | } 159 | 160 | if (!(font = msdfgen::loadFont(ft, font_file))) { 161 | msdfgen::deinitializeFreetype(ft); 162 | fprintf(stderr, "error: loadFont failed: filename=%s\n", font_file); 163 | exit(1); 164 | } 165 | 166 | /* 167 | * generate multi-channel SDF 168 | */ 169 | 170 | if (!msdfgen::loadGlyph(shape, font, codepoint, &glyphAdvance)) { 171 | msdfgen::deinitializeFreetype(ft); 172 | fprintf(stderr, "error: loadGlyph failed: codepoint=%d\n", codepoint); 173 | exit(1); 174 | } 175 | 176 | msdfgen::edgeColoringSimple(shape, angleThreshold, coloringSeed); 177 | msdfgen::generateMSDF(msdf, shape, range, scale, translate, 178 | scanlinePass ? 0 : edgeThreshold, overlapSupport); 179 | 180 | 181 | if (console) { 182 | render_block(msdf, width, height); 183 | } 184 | 185 | if (file) { 186 | image_ptr img = image::createBitmap(msdf.width(), msdf.height(), 187 | pixel_format_rgba); 188 | copy_bitmap(img, msdf); 189 | image::saveToFile(file, img); 190 | } 191 | 192 | msdfgen::destroyFont(font); 193 | msdfgen::deinitializeFreetype(ft); 194 | } -------------------------------------------------------------------------------- /tests/test0009.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define FLOAT32 "%.9g" 7 | 8 | struct atlas_ent 9 | { 10 | int bin_id, font_id, glyph; 11 | short x, y, ox, oy, w, h; 12 | }; 13 | 14 | int main(int argc, char **argv) 15 | { 16 | FILE *in = nullptr; 17 | 18 | if (argc != 2) { 19 | fprintf(stderr, "usage: %s \n", argv[0]); 20 | exit(1); 21 | } 22 | 23 | if (!(in = fopen(argv[1], "r"))) { 24 | fprintf(stderr, "error: fopen: %s, %s\n", argv[1], strerror(errno)); 25 | exit(1); 26 | } 27 | 28 | const int num_fields = 9; 29 | 30 | int ret; 31 | do { 32 | atlas_ent ent; 33 | ret = fscanf(in, "%d,%d,%d,%hd,%hd,%hd,%hd,%hd,%hd\n", 34 | &ent.bin_id, &ent.font_id, &ent.glyph, 35 | &ent.x, &ent.y, &ent.ox, &ent.oy, &ent.w, &ent.h); 36 | if (ret == num_fields) { 37 | printf("%d,%d,%d,%d,%d,%d,%d,%d,%d\n", 38 | ent.bin_id, ent.font_id, ent.glyph, 39 | ent.x, ent.y, ent.ox, ent.oy, ent.w, ent.h); 40 | } 41 | } while (ret == num_fields); 42 | 43 | fclose(in); 44 | } -------------------------------------------------------------------------------- /tests/test0010.cc: -------------------------------------------------------------------------------- 1 | #undef NDEBUG 2 | #include 3 | #include 4 | #include 5 | 6 | #include "glm/glm.hpp" 7 | #include "geometry.h" 8 | 9 | const struct { intersect_2d key; const char *name; } map[] = { 10 | 11 | { intersect_2d::none, "none" }, 12 | 13 | { intersect_2d::inner, "inner" }, 14 | { intersect_2d::north, "north" }, 15 | { intersect_2d::east, "east" }, 16 | { intersect_2d::south, "south" }, 17 | { intersect_2d::west, "west" }, 18 | 19 | { intersect_2d::north_east, "north_east" }, 20 | { intersect_2d::south_east, "south_east" }, 21 | { intersect_2d::south_west, "south_west" }, 22 | { intersect_2d::north_west, "north_west" }, 23 | 24 | { intersect_2d::north_south, "north_south" }, 25 | { intersect_2d::east_west, "east_west" }, 26 | 27 | { intersect_2d::left, "left" }, 28 | { intersect_2d::top, "top" }, 29 | { intersect_2d::bottom, "bottom" }, 30 | { intersect_2d::right, "right" }, 31 | { intersect_2d::surrounded, "surrounded" }, 32 | 33 | { intersect_2d::inner_north, "inner_north" }, 34 | { intersect_2d::inner_north_east, "inner_north_east" }, 35 | { intersect_2d::inner_east, "inner_east" }, 36 | { intersect_2d::inner_south_east, "inner_south_east" }, 37 | { intersect_2d::inner_south, "inner_south" }, 38 | { intersect_2d::inner_south_west, "inner_south_west" }, 39 | { intersect_2d::inner_west, "inner_west" }, 40 | { intersect_2d::inner_north_west, "inner_north_west" }, 41 | { intersect_2d::inner_north_south, "inner_north_south" }, 42 | { intersect_2d::inner_east_west, "inner_east_west" }, 43 | { intersect_2d::inner_left, "inner_left" }, 44 | { intersect_2d::inner_top, "inner_top" }, 45 | { intersect_2d::inner_bottom, "inner_bottom" }, 46 | { intersect_2d::inner_right, "inner_right" }, 47 | { intersect_2d::inner_surrounded, "inner_surrounded" }, 48 | 49 | { intersect_2d::none, nullptr }, 50 | }; 51 | 52 | std::string to_string(intersect_2d n) 53 | { 54 | for (auto *p = map; p->name; p++) { 55 | if (p->key == n) return p->name; 56 | } 57 | return "unknown"; 58 | } 59 | 60 | std::string to_string(rect_2d r) 61 | { 62 | char buf[64]; 63 | snprintf(buf, sizeof(buf), 64 | "{{%4.1f,%4.1f },{%4.1f,%4.1f }}", 65 | r.p0.x, r.p0.y, r.p1.x, r.p1.y); 66 | return std::string(buf); 67 | } 68 | 69 | void test(rect_2d a, rect_2d b, intersect_2d m) 70 | { 71 | intersect_2d n = intersect(a, b); 72 | printf("intersect(a: %s, b: %s) -> %-20s # %4s\n", 73 | to_string(a).c_str(), to_string(b).c_str(), 74 | to_string(m).c_str(), ((n&m) == m) ? "PASS" : "FAIL" 75 | ); 76 | assert((n&m) == m); 77 | } 78 | 79 | int main(int argc, char **argv) 80 | { 81 | test({{3,3},{6,6}}, {{2,2},{7,7}}, intersect_2d::inner); 82 | test({{4,1},{5,2}}, {{2,2},{7,7}}, intersect_2d::inner_north); 83 | test({{6,4},{8,5}}, {{2,2},{7,7}}, intersect_2d::inner_east); 84 | test({{4,6},{5,8}}, {{2,2},{7,7}}, intersect_2d::inner_south); 85 | test({{1,4},{3,5}}, {{2,2},{7,7}}, intersect_2d::inner_west); 86 | test({{6,1},{8,3}}, {{2,2},{7,7}}, intersect_2d::inner_north_east); 87 | test({{1,1},{3,3}}, {{2,2},{7,7}}, intersect_2d::inner_north_west); 88 | test({{6,6},{8,8}}, {{2,2},{7,7}}, intersect_2d::inner_south_east); 89 | test({{1,6},{3,8}}, {{2,2},{7,7}}, intersect_2d::inner_south_west); 90 | test({{4,1},{5,8}}, {{2,2},{7,7}}, intersect_2d::inner_north_south); 91 | test({{1,4},{8,5}}, {{2,2},{7,7}}, intersect_2d::inner_east_west); 92 | test({{1,1},{4,8}}, {{2,2},{7,7}}, intersect_2d::inner_left); 93 | test({{1,1},{8,3}}, {{2,2},{7,7}}, intersect_2d::inner_top); 94 | test({{6,1},{8,8}}, {{2,2},{7,7}}, intersect_2d::inner_right); 95 | test({{1,6},{8,8}}, {{2,2},{7,7}}, intersect_2d::inner_bottom); 96 | test({{1,1},{8,8}}, {{2,2},{7,7}}, intersect_2d::inner_surrounded); 97 | test({{4,0},{5,1}}, {{2,2},{7,7}}, intersect_2d::north); 98 | test({{8,4},{9,5}}, {{2,2},{7,7}}, intersect_2d::east); 99 | test({{4,8},{5,9}}, {{2,2},{7,7}}, intersect_2d::south); 100 | test({{0,4},{1,5}}, {{2,2},{7,7}}, intersect_2d::west); 101 | test({{8,0},{9,1}}, {{2,2},{7,7}}, intersect_2d::north_east); 102 | test({{0,0},{1,1}}, {{2,2},{7,7}}, intersect_2d::north_west); 103 | test({{8,8},{9,9}}, {{2,2},{7,7}}, intersect_2d::south_east); 104 | test({{0,8},{1,9}}, {{2,2},{7,7}}, intersect_2d::south_west); 105 | 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /tests/test0011.cc: -------------------------------------------------------------------------------- 1 | #undef NDEBUG 2 | #include 3 | #include 4 | 5 | #include "utf8.h" 6 | 7 | #include 8 | 9 | 10 | enum { 11 | emoji_block = 0x1F000, 12 | emoji_mask = ~0x00fff, 13 | emoji_flag = 0x1, 14 | }; 15 | 16 | struct utf8_range { uint64_t off; uint32_t len; uint32_t flags; }; 17 | 18 | /* 19 | * utf8_ranges_from_text 20 | * 21 | * scan text and return ranges of characters matching unicode blocks 22 | * 23 | * @param text to scan 24 | * @param length of text to scan 25 | * @param code to match on and split 26 | * @param mask to match on and split 27 | * @param flag to add to ranges that include the code 28 | */ 29 | 30 | std::vector utf8_ranges_from_text(const char* text, size_t length, 31 | uint32_t code, uint32_t mask, uint32_t flag) 32 | { 33 | std::vector vec; 34 | 35 | size_t i = 0, j = 0; 36 | bool last = false; 37 | while (i < length) 38 | { 39 | utf32_code cp = utf8_to_utf32_code(text + i); 40 | bool match = (cp.code & mask) == code; 41 | if (i != 0 && last != match || i == UINT_MAX ) { 42 | vec.push_back({ j, uint32_t(i - j), flag&-(uint32_t)last }); 43 | j = i; 44 | } 45 | last = match; 46 | i += cp.len; 47 | } 48 | if (i - j > 0) { 49 | vec.push_back({ j, uint32_t(i - j), flag&-(uint32_t)last }); 50 | } 51 | 52 | return vec; 53 | } 54 | 55 | /* 56 | * test splittng strings into ranges of emoji and non-emoji characters 57 | */ 58 | 59 | void test_find_emoji_ranges(size_t items, const char *text, 60 | std::initializer_list> l) 61 | { 62 | auto vec = utf8_ranges_from_text(text, strlen(text), 63 | emoji_block, emoji_mask, emoji_flag); 64 | 65 | auto li = l.begin(); 66 | for (auto &ent : vec) { 67 | assert(ent.off == li->first); 68 | assert(ent.len == li->second); 69 | li++; 70 | } 71 | assert(vec.size() == items); 72 | } 73 | 74 | int main(int argc, char **argv) 75 | { 76 | test_find_emoji_ranges(0, "", {}); 77 | test_find_emoji_ranges(1, "hello", {{0,5}}); 78 | test_find_emoji_ranges(1, "🙃😙", {{0,8}}); 79 | test_find_emoji_ranges(2, "hello🙃😙😃", {{0,5},{5,12}}); 80 | test_find_emoji_ranges(2, "🙃😙😃😜😍hello", {{0,20},{20,5}}); 81 | test_find_emoji_ranges(3, "hello😍hello", {{0,5},{5,4},{9,5}}); 82 | } -------------------------------------------------------------------------------- /tests/test0012.cc: -------------------------------------------------------------------------------- 1 | #undef NDEBUG 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "bitcode.h" 10 | 11 | 12 | /* 13 | * test cases 14 | */ 15 | 16 | static std::string to_binary(uint64_t symbol, size_t bit_width) 17 | { 18 | static const char* arr[] = { "▄", "▟", "▙", "█" }; 19 | std::string s; 20 | for (ssize_t i = bit_width-2; i >= 0; i-=2) { 21 | s.append(arr[(symbol>>i) & 3]); 22 | } 23 | return s; 24 | } 25 | 26 | void print_buffer(std::vector &buf) 27 | { 28 | ssize_t stride = 16; 29 | for (ssize_t i = 0; i < buf.size(); i += stride) { 30 | printf(" "); 31 | for (ssize_t j = i+stride-1; j >= i; j--) { 32 | if (j >= buf.size()) printf(" "); 33 | else printf(" 0x%02hhX", 34 | buf[j]); 35 | } 36 | printf("\n"); 37 | printf("%04zX: ", i & 0xffff); 38 | for (ssize_t j = i+stride-1; j >= i; j--) { 39 | if (j >= buf.size()) printf(" ░░░░"); 40 | else printf(" %s", to_binary(buf[j], 8).c_str()); 41 | } 42 | printf("\n"); 43 | } 44 | } 45 | 46 | void bitcode_test(vector_writer &vw, const char* name, const char* buf, size_t len) 47 | { 48 | printf("\n%s:\n", name); 49 | print_buffer(vw.buffer); 50 | assert(vw.buffer.size() == len); 51 | assert(memcmp(vw.buffer.data(), buf, len) == 0); 52 | } 53 | 54 | void test_bitcode_fixed_8_8_8() 55 | { 56 | vector_writer vw; 57 | vector_reader vr; 58 | bitcode_writer bw(&vw); 59 | bitcode_reader br(&vr); 60 | 61 | bw.write_fixed(0x0a, 8); 62 | bw.write_fixed(0x0b, 8); 63 | bw.write_fixed(0x0c, 8); 64 | bw.flush(); 65 | bitcode_test(vw, "fixed.8.8.8", "\x0A\x0B\x0C", 3); 66 | 67 | vr.set(vw.buffer); 68 | assert(br.read_fixed(8) == 0x0a); 69 | assert(br.read_fixed(8) == 0x0b); 70 | assert(br.read_fixed(8) == 0x0c); 71 | } 72 | 73 | void test_bitcode_fixed_32_32() 74 | { 75 | vector_writer vw; 76 | vector_reader vr; 77 | bitcode_writer bw(&vw); 78 | bitcode_reader br(&vr); 79 | 80 | bw.write_fixed(0xdeadbeef, 32); 81 | bw.write_fixed(0xfeedbeef, 32); 82 | bw.flush(); 83 | bitcode_test(vw, "fixed.32.32", "\xEF\xBE\xAD\xDE\xEF\xBE\xED\xFE", 8); 84 | 85 | vr.set(vw.buffer); 86 | assert(br.read_fixed(32) == 0xdeadbeef); 87 | assert(br.read_fixed(32) == 0xfeedbeef); 88 | } 89 | 90 | void test_bitcode_fixed_8_32_32_8() 91 | { 92 | vector_writer vw; 93 | vector_reader vr; 94 | bitcode_writer bw(&vw); 95 | bitcode_reader br(&vr); 96 | 97 | bw.write_fixed(0xff, 8); 98 | bw.write_fixed(0xdeadbeef, 32); 99 | bw.write_fixed(0xfeedbeef, 32); 100 | bw.write_fixed(0xff, 8); 101 | bw.flush(); 102 | bitcode_test(vw, "fixed.8.32.32.8", "\xFF\xEF\xBE\xAD\xDE\xEF\xBE\xED\xFE\xFF", 10); 103 | 104 | vr.set(vw.buffer); 105 | assert(br.read_fixed(8) == 0xff); 106 | assert(br.read_fixed(32) == 0xdeadbeef); 107 | assert(br.read_fixed(32) == 0xfeedbeef); 108 | assert(br.read_fixed(8) == 0xff); 109 | } 110 | 111 | void test_bitcode_fixed_64_64() 112 | { 113 | vector_writer vw; 114 | vector_reader vr; 115 | bitcode_writer bw(&vw); 116 | bitcode_reader br(&vr); 117 | 118 | bw.write_fixed(0x0001020304050607, 64); 119 | bw.write_fixed(0x08090a0b0c0d0e0f, 64); 120 | bw.flush(); 121 | bitcode_test(vw, "fixed.64.64", "\x07\x06\x05\x04\x03\x02\x01\x00\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08", 16); 122 | 123 | vr.set(vw.buffer); 124 | assert(br.read_fixed(64) == 0x0001020304050607); 125 | assert(br.read_fixed(64) == 0x08090a0b0c0d0e0f); 126 | } 127 | 128 | void test_bitcode_vlu_7() 129 | { 130 | vector_writer vw; 131 | vector_reader vr; 132 | bitcode_writer bw(&vw); 133 | bitcode_reader br(&vr); 134 | 135 | bw.write_vlu((1ull<<7)-1); 136 | bw.flush(); 137 | bitcode_test(vw, "vlu.7", "\xFE", 1); 138 | 139 | vr.set(vw.buffer); 140 | assert(br.read_vlu() == (1ull<<7)-1); 141 | } 142 | 143 | void test_bitcode_vlu_14() 144 | { 145 | vector_writer vw; 146 | vector_reader vr; 147 | bitcode_writer bw(&vw); 148 | bitcode_reader br(&vr); 149 | 150 | bw.write_vlu((1ull<<14)-1); 151 | bw.flush(); 152 | bitcode_test(vw, "vlu.14", "\xFD\xFF", 2); 153 | 154 | vr.set(vw.buffer); 155 | assert(br.read_vlu() == (1ull<<14)-1); 156 | } 157 | 158 | void test_bitcode_vlu_21() 159 | { 160 | vector_writer vw; 161 | vector_reader vr; 162 | bitcode_writer bw(&vw); 163 | bitcode_reader br(&vr); 164 | 165 | bw.write_vlu((1ull<<21)-1); 166 | bw.flush(); 167 | bitcode_test(vw, "vlu.21", "\xFB\xFF\xFF", 3); 168 | 169 | vr.set(vw.buffer); 170 | assert(br.read_vlu() == (1ull<<21)-1); 171 | } 172 | 173 | void test_bitcode_vlu_28() 174 | { 175 | vector_writer vw; 176 | vector_reader vr; 177 | bitcode_writer bw(&vw); 178 | bitcode_reader br(&vr); 179 | 180 | bw.write_vlu((1ull<<28)-1); 181 | bw.flush(); 182 | bitcode_test(vw, "vlu.28", "\xF7\xFF\xFF\xFF", 4); 183 | 184 | vr.set(vw.buffer); 185 | assert(br.read_vlu() == (1ull<<28)-1); 186 | } 187 | 188 | void test_bitcode_vlu_35() 189 | { 190 | vector_writer vw; 191 | vector_reader vr; 192 | bitcode_writer bw(&vw); 193 | bitcode_reader br(&vr); 194 | 195 | bw.write_vlu((1ull<<35)-1); 196 | bw.flush(); 197 | bitcode_test(vw, "vlu.35", "\xEF\xFF\xFF\xFF\xFF", 5); 198 | 199 | vr.set(vw.buffer); 200 | assert(br.read_vlu() == (1ull<<35)-1); 201 | } 202 | 203 | void test_bitcode_vlu_42() 204 | { 205 | vector_writer vw; 206 | vector_reader vr; 207 | bitcode_writer bw(&vw); 208 | bitcode_reader br(&vr); 209 | 210 | bw.write_vlu((1ull<<42)-1); 211 | bw.flush(); 212 | bitcode_test(vw, "vlu.42", "\xDF\xFF\xFF\xFF\xFF\xFF", 6); 213 | 214 | vr.set(vw.buffer); 215 | assert(br.read_vlu() == (1ull<<42)-1); 216 | } 217 | 218 | void test_bitcode_vlu_49() 219 | { 220 | vector_writer vw; 221 | vector_reader vr; 222 | bitcode_writer bw(&vw); 223 | bitcode_reader br(&vr); 224 | 225 | bw.write_vlu((1ull<<49)-1); 226 | bw.flush(); 227 | bitcode_test(vw, "vlu.49", "\xBF\xFF\xFF\xFF\xFF\xFF\xFF", 7); 228 | 229 | vr.set(vw.buffer); 230 | assert(br.read_vlu() == (1ull<<49)-1); 231 | } 232 | 233 | void test_bitcode_vlu_56() 234 | { 235 | vector_writer vw; 236 | vector_reader vr; 237 | bitcode_writer bw(&vw); 238 | bitcode_reader br(&vr); 239 | 240 | bw.write_vlu((1ull<<56)-1); 241 | bw.flush(); 242 | bitcode_test(vw, "vlu.56", "\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8); 243 | 244 | vr.set(vw.buffer); 245 | assert(br.read_vlu() == (1ull<<56)-1); 246 | } 247 | 248 | void test_bitcode_vlu_mixed() 249 | { 250 | vector_writer vw; 251 | vector_reader vr; 252 | bitcode_writer bw(&vw); 253 | bitcode_reader br(&vr); 254 | 255 | bw.write_vlu((1ull<<7)-1); 256 | bw.write_vlu((1ull<<14)-1); 257 | bw.write_vlu((1ull<<21)-1); 258 | bw.write_vlu((1ull<<28)-1); 259 | bw.write_vlu((1ull<<35)-1); 260 | bw.write_vlu((1ull<<42)-1); 261 | bw.write_vlu((1ull<<49)-1); 262 | bw.write_vlu((1ull<<56)-1); 263 | bw.flush(); 264 | bitcode_test(vw, "vlu.7.14.21.28.35.42.49.56", 265 | "\xFE\xFD\xFF\xFB\xFF\xFF\xF7\xFF" 266 | "\xFF\xFF\xEF\xFF\xFF\xFF\xFF\xDF" 267 | "\xFF\xFF\xFF\xFF\xFF\xBF\xFF\xFF" 268 | "\xFF\xFF\xFF\xFF\x7F\xFF\xFF\xFF" 269 | "\xFF\xFF\xFF\xFF", 36); 270 | 271 | vr.set(vw.buffer); 272 | assert(br.read_vlu() == (1ull<<7)-1); 273 | assert(br.read_vlu() == (1ull<<14)-1); 274 | assert(br.read_vlu() == (1ull<<21)-1); 275 | assert(br.read_vlu() == (1ull<<28)-1); 276 | assert(br.read_vlu() == (1ull<<35)-1); 277 | assert(br.read_vlu() == (1ull<<42)-1); 278 | assert(br.read_vlu() == (1ull<<49)-1); 279 | assert(br.read_vlu() == (1ull<<56)-1); 280 | } 281 | 282 | void test_bitcode() 283 | { 284 | test_bitcode_fixed_8_8_8(); 285 | test_bitcode_fixed_32_32(); 286 | test_bitcode_fixed_8_32_32_8(); 287 | test_bitcode_fixed_64_64(); 288 | test_bitcode_vlu_7(); 289 | test_bitcode_vlu_14(); 290 | test_bitcode_vlu_21(); 291 | test_bitcode_vlu_28(); 292 | test_bitcode_vlu_35(); 293 | test_bitcode_vlu_42(); 294 | test_bitcode_vlu_49(); 295 | test_bitcode_vlu_56(); 296 | test_bitcode_vlu_mixed(); 297 | } 298 | 299 | int main(int argc, char **argv) 300 | { 301 | test_bitcode(); 302 | } -------------------------------------------------------------------------------- /tests/test0013.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef _WIN32 4 | #include 5 | #else 6 | #include 7 | #endif 8 | 9 | #include "worker.h" 10 | 11 | #if defined _WIN32 12 | static bool _msleep(int millis) 13 | { 14 | Sleep(millis); 15 | return true; 16 | } 17 | #else 18 | static bool _msleep(int millis) 19 | { 20 | struct timespec ta = { millis / 1000, (millis % 1000) * 1000000 }; 21 | int ret = nanosleep(&ta, NULL); 22 | return ret == 0; 23 | } 24 | #endif 25 | 26 | /* 27 | * C++ threaded worker pool that executes work-items from a queue, featuring: 28 | * 29 | * - customizable work item (std::async has heavyweight items) 30 | * - customizable worker (std::async has not control over lifecycle) 31 | * - controllable concurrency (std::async concurrency is not controllable) 32 | */ 33 | 34 | static size_t next_mule_id; 35 | 36 | struct mule_item { size_t id; }; 37 | 38 | struct mule_worker : pool_worker 39 | { 40 | size_t mule_id; 41 | 42 | mule_worker() : mule_id(next_mule_id++) { 43 | printf("mule-%zu: began\n", mule_id); 44 | } 45 | 46 | virtual ~mule_worker() { 47 | printf("mule-%zu: finished\n", mule_id); 48 | } 49 | 50 | virtual void operator()(mule_item &item) { 51 | _msleep(1000); printf("mule-%zu item %zu\n", mule_id, item.id); 52 | } 53 | }; 54 | 55 | int main(int argc, const char **argv) 56 | { 57 | const size_t num_threads = std::thread::hardware_concurrency(); 58 | const size_t queue_size = num_threads * 2; 59 | 60 | pool_executor pool(num_threads, queue_size, [](){ 61 | return new mule_worker(); 62 | }); 63 | 64 | /* enqueue work items to the pool. 65 | * executor creates 'mule_worker' instances to process work items. */ 66 | for (size_t i = 0; i < num_threads * 2; i++ ) { 67 | pool.enqueue(mule_item{i}); 68 | } 69 | 70 | /* work completed after run(), which is an implicit control flow join, 71 | * not a full thread join; just a lightweight condition variable wake. */ 72 | pool.run(); 73 | 74 | return 0; 75 | } --------------------------------------------------------------------------------