├── .gitignore ├── LICENSE ├── README.md ├── extra ├── Inconsolata-Regular.ttf ├── OFL.txt ├── drawing.png ├── font.png ├── fragment_shader_text.fs └── vertex_shader_text.vs ├── include ├── KHR │ └── khrplatform.h ├── glad │ ├── glad.c │ └── glad.h ├── mv_easy_font.h ├── stb_image_write.h ├── stb_rect_pack.h └── stb_truetype.h └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | *.su 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mv_easy_font 2 | 3 | Simple font renderer library written in Opengl 3.3 using `stb_truetype.h` to load a packed bitmap into texture of a .ttf font. 4 | 5 | Uses instanced rendering, so that only position, index and color of each glyph have to be updated instead of updating two triangles of vertex attributes per glyph. 6 | 7 | Inspired by `stb_easy_font.h`. 8 | 9 | ### Usage 10 | Compile the example program with 11 | ```bash 12 | gcc main.c -Iinclude -lglfw -lm 13 | ``` 14 | or similar linking options for your OS (like -ldl in linux). Run with: 15 | ``` 16 | ./a.out path/to/font.ttf 17 | ``` 18 | or omit the file to use `extra/Inconsolata-Regular.ttf`. 19 | 20 | To use `mv_easy_font.h` in any opengl project, `#define MV_EASY_FONT_IMPLEMENTATION` before including `mv_easy_font.h` in one .c/.cpp file, and include `mv_easy_font.h` wherever else needed. Simply call `mv_ef_draw()` with the appropriate parameters to draw text: 21 | ```C 22 | mv_ef_draw(str, col, offset, font_size, res); 23 | ``` 24 | somewhere in the render loop. 25 | 26 | If 27 | ```C 28 | mv_ef_init(path_to_ttf, font_size, path_to_custom_vertex_shader, path_to_custom_fragment_shader); 29 | ``` 30 | is not called manually, it will try to look up a default font file using font size 48, and the built in shaders are used. All the three string arguments can be NULL to use default values. 31 | 32 | ### Dependencies 33 | 34 | `mv_easy_font.h` depends on `stb_truetype.h` and calls the OpenGL API, so make sure all the relevant OpenGL symbols and functions are loaded using something like GLEW, GLAD or whatever floats your boat. 35 | 36 | You can optionally choose to use include `stb_image_write.h` (for generating font.png) and `stb_rect_pack.h` (for more efficient packing) 37 | 38 | At the moment there are two shader files that need to be visible to the executable. These will probably be inlined in the near future. 39 | 40 | console.ttf is hardcoded at the moment, so that will also have to be available in the current working directory when running. 41 | 42 | The example code uses GLFW and GLAD, but those should not be required, as long as opengl symbols are loaded properly. 43 | 44 | ### TODO 45 | 46 | - ADd functionality to adjust the bitmap texture size based on the font size. currently it's using a hard-coded texture that we hope will fit (and is proably too big). Might not even care, since it doesn't really impact performance... 47 | 48 | - Look into padding in the bitmap 49 | 50 | ### Screenshot: 51 | ![screenshot](http://i.imgur.com/zSHHVo7.png) 52 | 53 | ### Font bitmap 54 | 55 | ![font](extra/font.png) 56 | -------------------------------------------------------------------------------- /extra/Inconsolata-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vassvik/mv_easy_font/ea0f9b17cdd0667cbdfff0117a8f66eb11e8ca03/extra/Inconsolata-Regular.ttf -------------------------------------------------------------------------------- /extra/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2006 The Inconsolata Project Authors 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /extra/drawing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vassvik/mv_easy_font/ea0f9b17cdd0667cbdfff0117a8f66eb11e8ca03/extra/drawing.png -------------------------------------------------------------------------------- /extra/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vassvik/mv_easy_font/ea0f9b17cdd0667cbdfff0117a8f66eb11e8ca03/extra/font.png -------------------------------------------------------------------------------- /extra/fragment_shader_text.fs: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | in vec2 uv; 4 | in float color_index; 5 | 6 | uniform sampler2D sampler_font; 7 | uniform sampler1D sampler_colors; 8 | uniform float num_colors; 9 | 10 | out vec4 color; 11 | 12 | void main() 13 | { 14 | vec3 col = texture(sampler_colors, (color_index+0.5)/num_colors).rgb; 15 | float s = texture(sampler_font, uv).r; 16 | color = vec4(col, s); 17 | } 18 | -------------------------------------------------------------------------------- /extra/vertex_shader_text.vs: -------------------------------------------------------------------------------- 1 | #version 330 core // () 2 | 3 | layout(location = 0) in vec2 vertexPosition; 4 | layout(location = 1) in vec4 instanceGlyph; 5 | 6 | uniform sampler2D sampler_font; 7 | uniform sampler2D sampler_meta; 8 | 9 | uniform float offset_firstline; // ascent - descent - linegap/2 10 | uniform float scale_factor; // scaling factor proportional to font size 11 | uniform vec2 string_offset; // offset of upper-left corner 12 | 13 | uniform vec2 res_meta; // 96x2 14 | uniform vec2 res_bitmap; // 512x256 15 | uniform vec2 resolution; // screen resolution 16 | 17 | out vec2 uv; 18 | out float color_index; // for syntax highlighting 19 | 20 | void main() 21 | { 22 | // (xoff, yoff, xoff2, yoff2), from second row of texture 23 | vec4 q2 = texture(sampler_meta, vec2((instanceGlyph.z + 0.5)/res_meta.x, 0.75))*vec4(res_bitmap, res_bitmap); 24 | 25 | vec2 p = vertexPosition*(q2.zw - q2.xy) + q2.xy; // offset and scale it properly relative to baseline 26 | p *= vec2(1.0, -1.0); // flip y, since texture is upside-down 27 | p.y -= offset_firstline; // make sure the upper-left corner of the string is in the upper-left corner of the screen 28 | p *= scale_factor; // scale relative to font size 29 | p += instanceGlyph.xy + string_offset; // move glyph into the right position 30 | p *= 2.0/resolution; // to NDC 31 | p += vec2(-1.0, 1.0); // move to upper-left corner instead of center 32 | 33 | gl_Position = vec4(p, 0.0, 1.0); 34 | 35 | // (x0, y0, x1-x0, y1-y0), from first row of texture 36 | vec4 q = texture(sampler_meta, vec2((instanceGlyph.z + 0.5)/res_meta.x, 0.25)); 37 | 38 | // send the correct uv's in the font atlas to the fragment shader 39 | uv = q.xy + vertexPosition*q.zw; 40 | color_index = instanceGlyph.w; 41 | } 42 | 43 | -------------------------------------------------------------------------------- /include/KHR/khrplatform.h: -------------------------------------------------------------------------------- 1 | #ifndef __khrplatform_h_ 2 | #define __khrplatform_h_ 3 | 4 | /* 5 | ** Copyright (c) 2008-2009 The Khronos Group Inc. 6 | ** 7 | ** Permission is hereby granted, free of charge, to any person obtaining a 8 | ** copy of this software and/or associated documentation files (the 9 | ** "Materials"), to deal in the Materials without restriction, including 10 | ** without limitation the rights to use, copy, modify, merge, publish, 11 | ** distribute, sublicense, and/or sell copies of the Materials, and to 12 | ** permit persons to whom the Materials are furnished to do so, subject to 13 | ** the following conditions: 14 | ** 15 | ** The above copyright notice and this permission notice shall be included 16 | ** in all copies or substantial portions of the Materials. 17 | ** 18 | ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 25 | */ 26 | 27 | /* Khronos platform-specific types and definitions. 28 | * 29 | * $Revision: 32517 $ on $Date: 2016-03-11 02:41:19 -0800 (Fri, 11 Mar 2016) $ 30 | * 31 | * Adopters may modify this file to suit their platform. Adopters are 32 | * encouraged to submit platform specific modifications to the Khronos 33 | * group so that they can be included in future versions of this file. 34 | * Please submit changes by sending them to the public Khronos Bugzilla 35 | * (http://khronos.org/bugzilla) by filing a bug against product 36 | * "Khronos (general)" component "Registry". 37 | * 38 | * A predefined template which fills in some of the bug fields can be 39 | * reached using http://tinyurl.com/khrplatform-h-bugreport, but you 40 | * must create a Bugzilla login first. 41 | * 42 | * 43 | * See the Implementer's Guidelines for information about where this file 44 | * should be located on your system and for more details of its use: 45 | * http://www.khronos.org/registry/implementers_guide.pdf 46 | * 47 | * This file should be included as 48 | * #include 49 | * by Khronos client API header files that use its types and defines. 50 | * 51 | * The types in khrplatform.h should only be used to define API-specific types. 52 | * 53 | * Types defined in khrplatform.h: 54 | * khronos_int8_t signed 8 bit 55 | * khronos_uint8_t unsigned 8 bit 56 | * khronos_int16_t signed 16 bit 57 | * khronos_uint16_t unsigned 16 bit 58 | * khronos_int32_t signed 32 bit 59 | * khronos_uint32_t unsigned 32 bit 60 | * khronos_int64_t signed 64 bit 61 | * khronos_uint64_t unsigned 64 bit 62 | * khronos_intptr_t signed same number of bits as a pointer 63 | * khronos_uintptr_t unsigned same number of bits as a pointer 64 | * khronos_ssize_t signed size 65 | * khronos_usize_t unsigned size 66 | * khronos_float_t signed 32 bit floating point 67 | * khronos_time_ns_t unsigned 64 bit time in nanoseconds 68 | * khronos_utime_nanoseconds_t unsigned time interval or absolute time in 69 | * nanoseconds 70 | * khronos_stime_nanoseconds_t signed time interval in nanoseconds 71 | * khronos_boolean_enum_t enumerated boolean type. This should 72 | * only be used as a base type when a client API's boolean type is 73 | * an enum. Client APIs which use an integer or other type for 74 | * booleans cannot use this as the base type for their boolean. 75 | * 76 | * Tokens defined in khrplatform.h: 77 | * 78 | * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. 79 | * 80 | * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. 81 | * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. 82 | * 83 | * Calling convention macros defined in this file: 84 | * KHRONOS_APICALL 85 | * KHRONOS_APIENTRY 86 | * KHRONOS_APIATTRIBUTES 87 | * 88 | * These may be used in function prototypes as: 89 | * 90 | * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( 91 | * int arg1, 92 | * int arg2) KHRONOS_APIATTRIBUTES; 93 | */ 94 | 95 | /*------------------------------------------------------------------------- 96 | * Definition of KHRONOS_APICALL 97 | *------------------------------------------------------------------------- 98 | * This precedes the return type of the function in the function prototype. 99 | */ 100 | #if defined(_WIN32) && !defined(__SCITECH_SNAP__) 101 | # define KHRONOS_APICALL __declspec(dllimport) 102 | #elif defined (__SYMBIAN32__) 103 | # define KHRONOS_APICALL IMPORT_C 104 | #elif defined(__ANDROID__) 105 | # include 106 | # define KHRONOS_APICALL __attribute__((visibility("default"))) __NDK_FPABI__ 107 | #else 108 | # define KHRONOS_APICALL 109 | #endif 110 | 111 | /*------------------------------------------------------------------------- 112 | * Definition of KHRONOS_APIENTRY 113 | *------------------------------------------------------------------------- 114 | * This follows the return type of the function and precedes the function 115 | * name in the function prototype. 116 | */ 117 | #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) 118 | /* Win32 but not WinCE */ 119 | # define KHRONOS_APIENTRY __stdcall 120 | #else 121 | # define KHRONOS_APIENTRY 122 | #endif 123 | 124 | /*------------------------------------------------------------------------- 125 | * Definition of KHRONOS_APIATTRIBUTES 126 | *------------------------------------------------------------------------- 127 | * This follows the closing parenthesis of the function prototype arguments. 128 | */ 129 | #if defined (__ARMCC_2__) 130 | #define KHRONOS_APIATTRIBUTES __softfp 131 | #else 132 | #define KHRONOS_APIATTRIBUTES 133 | #endif 134 | 135 | /*------------------------------------------------------------------------- 136 | * basic type definitions 137 | *-----------------------------------------------------------------------*/ 138 | #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) 139 | 140 | 141 | /* 142 | * Using 143 | */ 144 | #include 145 | typedef int32_t khronos_int32_t; 146 | typedef uint32_t khronos_uint32_t; 147 | typedef int64_t khronos_int64_t; 148 | typedef uint64_t khronos_uint64_t; 149 | #define KHRONOS_SUPPORT_INT64 1 150 | #define KHRONOS_SUPPORT_FLOAT 1 151 | 152 | #elif defined(__VMS ) || defined(__sgi) 153 | 154 | /* 155 | * Using 156 | */ 157 | #include 158 | typedef int32_t khronos_int32_t; 159 | typedef uint32_t khronos_uint32_t; 160 | typedef int64_t khronos_int64_t; 161 | typedef uint64_t khronos_uint64_t; 162 | #define KHRONOS_SUPPORT_INT64 1 163 | #define KHRONOS_SUPPORT_FLOAT 1 164 | 165 | #elif defined(_WIN32) && !defined(__SCITECH_SNAP__) 166 | 167 | /* 168 | * Win32 169 | */ 170 | typedef __int32 khronos_int32_t; 171 | typedef unsigned __int32 khronos_uint32_t; 172 | typedef __int64 khronos_int64_t; 173 | typedef unsigned __int64 khronos_uint64_t; 174 | #define KHRONOS_SUPPORT_INT64 1 175 | #define KHRONOS_SUPPORT_FLOAT 1 176 | 177 | #elif defined(__sun__) || defined(__digital__) 178 | 179 | /* 180 | * Sun or Digital 181 | */ 182 | typedef int khronos_int32_t; 183 | typedef unsigned int khronos_uint32_t; 184 | #if defined(__arch64__) || defined(_LP64) 185 | typedef long int khronos_int64_t; 186 | typedef unsigned long int khronos_uint64_t; 187 | #else 188 | typedef long long int khronos_int64_t; 189 | typedef unsigned long long int khronos_uint64_t; 190 | #endif /* __arch64__ */ 191 | #define KHRONOS_SUPPORT_INT64 1 192 | #define KHRONOS_SUPPORT_FLOAT 1 193 | 194 | #elif 0 195 | 196 | /* 197 | * Hypothetical platform with no float or int64 support 198 | */ 199 | typedef int khronos_int32_t; 200 | typedef unsigned int khronos_uint32_t; 201 | #define KHRONOS_SUPPORT_INT64 0 202 | #define KHRONOS_SUPPORT_FLOAT 0 203 | 204 | #else 205 | 206 | /* 207 | * Generic fallback 208 | */ 209 | #include 210 | typedef int32_t khronos_int32_t; 211 | typedef uint32_t khronos_uint32_t; 212 | typedef int64_t khronos_int64_t; 213 | typedef uint64_t khronos_uint64_t; 214 | #define KHRONOS_SUPPORT_INT64 1 215 | #define KHRONOS_SUPPORT_FLOAT 1 216 | 217 | #endif 218 | 219 | 220 | /* 221 | * Types that are (so far) the same on all platforms 222 | */ 223 | typedef signed char khronos_int8_t; 224 | typedef unsigned char khronos_uint8_t; 225 | typedef signed short int khronos_int16_t; 226 | typedef unsigned short int khronos_uint16_t; 227 | 228 | /* 229 | * Types that differ between LLP64 and LP64 architectures - in LLP64, 230 | * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears 231 | * to be the only LLP64 architecture in current use. 232 | */ 233 | #ifdef _WIN64 234 | typedef signed long long int khronos_intptr_t; 235 | typedef unsigned long long int khronos_uintptr_t; 236 | typedef signed long long int khronos_ssize_t; 237 | typedef unsigned long long int khronos_usize_t; 238 | #else 239 | typedef signed long int khronos_intptr_t; 240 | typedef unsigned long int khronos_uintptr_t; 241 | typedef signed long int khronos_ssize_t; 242 | typedef unsigned long int khronos_usize_t; 243 | #endif 244 | 245 | #if KHRONOS_SUPPORT_FLOAT 246 | /* 247 | * Float type 248 | */ 249 | typedef float khronos_float_t; 250 | #endif 251 | 252 | #if KHRONOS_SUPPORT_INT64 253 | /* Time types 254 | * 255 | * These types can be used to represent a time interval in nanoseconds or 256 | * an absolute Unadjusted System Time. Unadjusted System Time is the number 257 | * of nanoseconds since some arbitrary system event (e.g. since the last 258 | * time the system booted). The Unadjusted System Time is an unsigned 259 | * 64 bit value that wraps back to 0 every 584 years. Time intervals 260 | * may be either signed or unsigned. 261 | */ 262 | typedef khronos_uint64_t khronos_utime_nanoseconds_t; 263 | typedef khronos_int64_t khronos_stime_nanoseconds_t; 264 | #endif 265 | 266 | /* 267 | * Dummy value used to pad enum types to 32 bits. 268 | */ 269 | #ifndef KHRONOS_MAX_ENUM 270 | #define KHRONOS_MAX_ENUM 0x7FFFFFFF 271 | #endif 272 | 273 | /* 274 | * Enumerated boolean type 275 | * 276 | * Values other than zero should be considered to be true. Therefore 277 | * comparisons should not be made against KHRONOS_TRUE. 278 | */ 279 | typedef enum { 280 | KHRONOS_FALSE = 0, 281 | KHRONOS_TRUE = 1, 282 | KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM 283 | } khronos_boolean_enum_t; 284 | 285 | #endif /* __khrplatform_h_ */ 286 | -------------------------------------------------------------------------------- /include/mv_easy_font.h: -------------------------------------------------------------------------------- 1 | #ifndef MV_EASY_FONT_H 2 | #define MV_EASY_FONT_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | // utility functions to load shaders 8 | char *mv_ef_read_entire_file(const char *filename); 9 | GLuint mv_ef_load_shaders(const char *vs_path, const char *fs_path); 10 | 11 | #define MAX_STRING_LEN 40000 // more glyphs than any reasonable person would show on the screen at once. you can only fit 20736 10x10 rects in a 1920x1080 window 12 | #define NUM_GLYPHS 96 13 | 14 | // 15 | // Struct containing font info and OpenGL variables for vbos, vao and textures 16 | // 17 | typedef struct { 18 | int initialized; // to be able to initialize from the first call to mv_ef_draw() 19 | 20 | char filename[256]; 21 | 22 | // character info 23 | // filled up by stb_truetype.h 24 | stbtt_packedchar cdata[96]; 25 | 26 | // font info and data 27 | int height; // bitmap height 28 | int width; // bitmap width 29 | float font_size; // font size in pixels 30 | 31 | // displacement info 32 | float ascent; // max distance above baseline for all glyphs 33 | float descent; // max distance below baseline for all glyphs 34 | float linegap; // distance betwen ascent of next line and descent of current line 35 | float linedist; // distance between the baseline of two lines 36 | 37 | // opengl stuff 38 | GLuint vao; 39 | GLuint program; 40 | 41 | // font bitmap texture 42 | // generated using stb_truetype.h 43 | GLuint texture_fontdata; 44 | 45 | // metadata texture. 46 | // first row contains information on which parts of the bitmap correspond to a glyph. 47 | // the second row contain information about the relative displacement of the glyph relative to the cursor position 48 | GLuint texture_metadata; 49 | 50 | // color texture 51 | // used to color each glyph individually, e.g. for syntax highlighting 52 | GLuint texture_colors; 53 | 54 | // vbos 55 | GLuint vbo_quad; // vec2: simply just a regular [0,1]x[0,1] quad 56 | GLuint vbo_instances; // vec4: (char_pos_x, char_pos_y, char_index, color_index) 57 | } mv_ef_font; 58 | 59 | void mv_ef_init(char *filename, int font_size, char *vs_filename, char *fs_filename); 60 | void mv_ef_draw(char *str, char *col, float offset[2], float size); 61 | void mv_ef_string_dimensions(char *str, float *width, float *height, int font_size); 62 | void mv_ef_set_colors(unsigned char *colors); 63 | unsigned char *mv_ef_get_colors(int *num_colors); 64 | mv_ef_font *mv_ef_get_font(); 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif 69 | 70 | #endif // MV_EASY_FONT_H 71 | 72 | 73 | #if defined(MV_EASY_FONT_IMPLEMENTATION) && defined(STB_TRUETYPE_IMPLEMENTATION) 74 | 75 | 76 | #define mv_ef_num_colors 256 77 | 78 | // @TODO: Add larger color palette. currently only 9 colors are filled in 79 | unsigned char mv_ef_colors[mv_ef_num_colors*3] = { 80 | 248, 248, 242, // foreground color 81 | 249, 38, 114, // operator 82 | 174, 129, 255, // numeric 83 | 102, 217, 239, // function 84 | 249, 38, 114, // keyword 85 | 117, 113, 94, // comment 86 | 102, 217, 239, // type 87 | 73, 72, 62, // background color 88 | 39, 40, 34 // clear color 89 | }; 90 | 91 | 92 | // private global variable that all the functions use. 93 | mv_ef_font font = {0}; 94 | 95 | // 96 | // Return the whole font struct, in case the user want access to individual data in it 97 | // 98 | mv_ef_font *mv_ef_get_font() 99 | { 100 | return &font; 101 | } 102 | 103 | unsigned char *mv_ef_get_colors(int *num_colors) 104 | { 105 | *num_colors = mv_ef_num_colors; 106 | return mv_ef_colors; 107 | } 108 | 109 | void mv_ef_set_colors(unsigned char *colors) 110 | { 111 | for (int i = 0; i < mv_ef_num_colors*3; i++) 112 | mv_ef_colors[i] = colors[i]; 113 | 114 | glActiveTexture(GL_TEXTURE2); 115 | glBindTexture(GL_TEXTURE_1D, font.texture_colors); 116 | glTexSubImage1D(GL_TEXTURE_1D, 0, 0, mv_ef_num_colors, GL_RGB, GL_UNSIGNED_BYTE, mv_ef_colors); 117 | } 118 | 119 | // 120 | // Calculates the size of a string, in the pixel size specified. 121 | // Note: Stray newlines are also counted 122 | // 123 | void mv_ef_string_dimensions(char *str, float *width, float *height, int font_size) 124 | { 125 | float X = 0; 126 | float Y = 0; 127 | 128 | int W = 0; 129 | char *ptr = str; 130 | while (*ptr) { 131 | if (*ptr == '\n') { 132 | if (X > W) 133 | W = X; 134 | X = 0; 135 | Y++; 136 | } else { 137 | X += (font.cdata[*ptr-32].xadvance)*font_size/font.font_size; 138 | } 139 | ptr++; 140 | } 141 | 142 | // if it ended on a line with no newline 143 | if (X != 0) { 144 | Y++; 145 | if (W == 0) 146 | W = X; 147 | 148 | if (X > W) 149 | W = X; 150 | } 151 | 152 | *width = W; 153 | *height = Y*(font.linedist)*font_size/font.font_size; 154 | } 155 | 156 | // 157 | // Reads and compiles the shaders 158 | // 159 | // Calls stb_truetype.h routines to read and parse a .ttf file, 160 | // creates a bitmap that is uploaded to the gpu using opengl 161 | // 162 | // calculates and saves a bunch of useful variables and put them in the global font variable 163 | // 164 | void mv_ef_init(char *filename, int font_size, char *vs_filename, char *fs_filename) 165 | { 166 | font.initialized = 1; 167 | 168 | font.program = mv_ef_load_shaders(vs_filename, fs_filename); 169 | 170 | // load .ttf into a bitmap using stb_truetype.h 171 | font.width = 512; 172 | font.height = 512; 173 | font.font_size = font_size; 174 | 175 | // Read the data from file 176 | int ttf_size_max = 1e6; 177 | unsigned char *ttf_buffer = (unsigned char*)malloc(ttf_size_max); // sufficient size for consola.ttf 178 | 179 | const char *ttf_filenames[] = { 180 | "extra/Inconsolata-Regular.ttf", 181 | "Inconsolata-Regular.ttf", 182 | "C:/Windows/Fonts/consola.ttf", 183 | "/usr/share/fonts/dejavu/DejaVuSansMono.ttf", 184 | }; 185 | 186 | 187 | FILE *fp; 188 | if ((fp = fopen(filename, "rb"))) { 189 | strcpy(font.filename, filename); 190 | } else { 191 | int found = 0; 192 | for (int i = 0; i < 4; i++) { 193 | if ((fp = fopen(ttf_filenames[i], "rb"))) { 194 | strcpy(font.filename, ttf_filenames[i]); 195 | found = 1; 196 | break; 197 | } 198 | } 199 | 200 | if (!found) { 201 | printf("I give up. Can't find a valid .ttf file. Exiting.\n"); 202 | exit(-9); 203 | } 204 | } 205 | 206 | printf("Using font file: \"%s\"\n", font.filename); 207 | 208 | fread(ttf_buffer, 1, ttf_size_max, fp); 209 | fclose(fp); 210 | 211 | // Pack and create bitmap 212 | unsigned char *bitmap = (unsigned char*)malloc(font.height*font.width); 213 | stbtt_pack_context pc; 214 | stbtt_PackBegin(&pc, bitmap, font.width, font.height, 0, 1, NULL); 215 | stbtt_PackSetOversampling(&pc, 1, 1); 216 | stbtt_PackFontRange(&pc, ttf_buffer, 0, font.font_size, 32, 96, font.cdata); 217 | stbtt_PackEnd(&pc); 218 | 219 | #ifdef STB_IMAGE_WRITE_IMPLEMENTATION 220 | stbi_write_png("font.png", font.width, font.height, 1, bitmap, 0); 221 | #endif 222 | 223 | // calculate vertical font metrics 224 | stbtt_fontinfo info; 225 | stbtt_InitFont(&info, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); 226 | 227 | float s = stbtt_ScaleForPixelHeight(&info, font.font_size); 228 | int a, d, l; 229 | stbtt_GetFontVMetrics(&info, &a, &d, &l); 230 | 231 | font.ascent = a*s; 232 | font.descent = d*s; 233 | font.linegap = l*s; 234 | font.linedist = font.ascent - font.descent + font.linegap; 235 | 236 | free(ttf_buffer); 237 | 238 | // output char metrics per char 239 | int max_y1 = 0; // for truncating packed texture if nescessary 240 | for (int i = 0; i < 96; i++) { 241 | /* 242 | printf("%3d %2c: (%3u, %3u, %3u, %3u), %+6.2f, %+6.2f, %+6.2f, %+6.2f, %f\n", i, i+32, 243 | font.cdata[i].x0, font.cdata[i].y0, 244 | font.cdata[i].x1, font.cdata[i].y1, 245 | font.cdata[i].xoff, font.cdata[i].yoff, 246 | font.cdata[i].xoff2, font.cdata[i].yoff2, 247 | font.cdata[i].xadvance); 248 | */ 249 | if (font.cdata[i].y1 > max_y1) 250 | max_y1 = font.cdata[i].y1; 251 | } 252 | 253 | // cut away the unused part of the bitmap 254 | font.height = max_y1+1; 255 | 256 | // vaos 257 | glGenVertexArrays(1, &font.vao); 258 | glBindVertexArray(font.vao); 259 | 260 | // quad vbo setup, used for glyph vertex positions, 261 | // just uv coordinates that will be stretched accordingly by the glyphs width and height 262 | float v[] = {0.0, 0.0, 263 | 1.0, 0.0, 264 | 0.0, 1.0, 265 | 0.0, 1.0, 266 | 1.0, 0.0, 267 | 1.0, 1.0}; 268 | 269 | glGenBuffers(1, &font.vbo_quad); 270 | glBindBuffer(GL_ARRAY_BUFFER, font.vbo_quad); 271 | glBufferData(GL_ARRAY_BUFFER, sizeof(v), v, GL_STATIC_DRAW); 272 | 273 | glEnableVertexAttribArray(0); 274 | glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,0,(void*)0); 275 | glVertexAttribDivisor(0, 0); 276 | 277 | // instance vbo setup. 278 | // for glyph positions, glyph index and color index 279 | glGenBuffers(1, &font.vbo_instances); 280 | glBindBuffer(GL_ARRAY_BUFFER, font.vbo_instances); 281 | glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*MAX_STRING_LEN, NULL, GL_DYNAMIC_DRAW); 282 | 283 | glEnableVertexAttribArray(1); 284 | glVertexAttribPointer(1,4,GL_FLOAT,GL_FALSE,0,(void*)0); 285 | glVertexAttribDivisor(1, 1); 286 | //glEnable(GL_FRAMEBUFFER_SRGB); 287 | // setup and upload font bitmap texture 288 | glGenTextures(1, &font.texture_fontdata); 289 | glActiveTexture(GL_TEXTURE0); 290 | glBindTexture(GL_TEXTURE_2D, font.texture_fontdata); 291 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, font.width, font.height, 0, GL_RED, GL_UNSIGNED_BYTE, bitmap); 292 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 293 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 294 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 295 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 296 | 297 | free(bitmap); 298 | 299 | // setup and upload font metadata texture 300 | // used for lookup in the bitmap texture 301 | glGenTextures(1, &font.texture_metadata); 302 | glActiveTexture(GL_TEXTURE1); 303 | glBindTexture(GL_TEXTURE_2D, font.texture_metadata); 304 | 305 | float *texture_metadata = (float*)malloc(sizeof(float)*8*NUM_GLYPHS); 306 | 307 | for (int i = 0; i < NUM_GLYPHS; i++) { 308 | int k1 = 0*NUM_GLYPHS + i; 309 | int k2 = 1*NUM_GLYPHS + i; 310 | texture_metadata[4*k1+0] = font.cdata[i].x0/(double)font.width; 311 | texture_metadata[4*k1+1] = font.cdata[i].y0/(double)font.height; 312 | texture_metadata[4*k1+2] = (font.cdata[i].x1-font.cdata[i].x0)/(double)font.width; 313 | texture_metadata[4*k1+3] = (font.cdata[i].y1-font.cdata[i].y0)/(double)font.height; 314 | 315 | texture_metadata[4*k2+0] = font.cdata[i].xoff/(double)font.width; 316 | texture_metadata[4*k2+1] = font.cdata[i].yoff/(double)font.height; 317 | texture_metadata[4*k2+2] = font.cdata[i].xoff2/(double)font.width; 318 | texture_metadata[4*k2+3] = font.cdata[i].yoff2/(double)font.height; 319 | } 320 | 321 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 322 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 323 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 324 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 325 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, NUM_GLYPHS, 2, 0, GL_RGBA, GL_FLOAT, texture_metadata); 326 | 327 | free(texture_metadata); 328 | 329 | // setup color texture 330 | //glUniform3fv(glGetUniformLocation(font.program, "colors"), 9, mv_ef_colors); 331 | 332 | glGenTextures(1, &font.texture_colors); 333 | glActiveTexture(GL_TEXTURE2); 334 | glBindTexture(GL_TEXTURE_1D, font.texture_colors); 335 | glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, mv_ef_num_colors, 0, GL_RGB, GL_UNSIGNED_BYTE, mv_ef_colors); 336 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 337 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 338 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); 339 | 340 | // upload constant uniforms 341 | glUseProgram(font.program); 342 | glUniform1i(glGetUniformLocation(font.program, "sampler_font"), 0); 343 | glUniform1i(glGetUniformLocation(font.program, "sampler_meta"), 1); 344 | glUniform1i(glGetUniformLocation(font.program, "sampler_colors"), 2); 345 | 346 | glUniform2f(glGetUniformLocation(font.program, "res_bitmap"), font.width, font.height); 347 | glUniform2f(glGetUniformLocation(font.program, "res_meta"), NUM_GLYPHS, 2); 348 | glUniform1f(glGetUniformLocation(font.program, "num_colors"), mv_ef_num_colors); 349 | glUniform1f(glGetUniformLocation(font.program, "offset_firstline"), font.linedist-font.linegap); 350 | } 351 | 352 | // 353 | // draw a string 354 | // 355 | // will call mv_ef_init() if it's the first time it's called. 356 | // can optionally call this manually 357 | // 358 | // will parse the string and update the instance vbo, then upload it 359 | // 360 | // finally draws 361 | // 362 | void mv_ef_draw(char *str, char *col, float offset[2], float size) 363 | { 364 | static float text_glyph_data[4*MAX_STRING_LEN] = {0}; 365 | 366 | if (font.initialized == 0) { 367 | mv_ef_init(NULL, 48.0, NULL, NULL); 368 | } 369 | 370 | int len = strlen(str); 371 | 372 | if (len > MAX_STRING_LEN) { 373 | printf("Error: string too long. Returning\n"); 374 | return; 375 | } 376 | 377 | // parse string, convert to vbo data 378 | float X = 0.0; 379 | float Y = 0.0; 380 | float l = font.linedist*size/font.font_size; 381 | 382 | float advances[96]; 383 | for (int i = 0; i < 96; i++) 384 | advances[i] = font.cdata[i].xadvance*size/font.font_size; 385 | 386 | float *t = text_glyph_data; 387 | for (char *c = str; *c; c++) { 388 | 389 | if ((*c) == '\n') { 390 | X = 0.0; 391 | Y -= l; 392 | continue; 393 | } 394 | 395 | int code_base = (*c)-32; // first glyph is ' ', i.e. ascii code 32 396 | float dx = advances[code_base]; 397 | 398 | *t++ = X; 399 | *t++ = Y; 400 | *t++ = code_base; 401 | *t++ = col ? col[c-str] : 0; 402 | 403 | X += dx; 404 | } 405 | int ctr = (t - text_glyph_data)/4; 406 | 407 | // Backup GL state 408 | GLint last_program, last_vertex_array; 409 | GLint last_texture0, last_texture1, last_texture2; 410 | GLint last_blend_src, last_blend_dst; 411 | GLint last_blend_equation_rgb, last_blend_equation_alpha; 412 | 413 | glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); 414 | glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); 415 | 416 | glActiveTexture(GL_TEXTURE0); 417 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture0); 418 | glActiveTexture(GL_TEXTURE1); 419 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture1); 420 | glActiveTexture(GL_TEXTURE2); 421 | glGetIntegerv(GL_TEXTURE_BINDING_1D, &last_texture2); 422 | 423 | glGetIntegerv(GL_BLEND_SRC, &last_blend_src); 424 | glGetIntegerv(GL_BLEND_DST, &last_blend_dst); 425 | glGetIntegerv(GL_BLEND_EQUATION_RGB, &last_blend_equation_rgb); 426 | glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &last_blend_equation_alpha); 427 | 428 | GLboolean last_enable_blend = glIsEnabled(GL_BLEND); 429 | GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); 430 | 431 | // Setup render state: alpha-blending enabled, no depth testing and bind textures 432 | glEnable(GL_BLEND); 433 | glBlendEquation(GL_FUNC_ADD); 434 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 435 | 436 | glDisable(GL_DEPTH_TEST); 437 | 438 | glActiveTexture(GL_TEXTURE0); 439 | glBindTexture(GL_TEXTURE_2D, font.texture_fontdata); 440 | glActiveTexture(GL_TEXTURE1); 441 | glBindTexture(GL_TEXTURE_2D, font.texture_metadata); 442 | glActiveTexture(GL_TEXTURE2); 443 | glBindTexture(GL_TEXTURE_1D, font.texture_colors); 444 | 445 | // update bindings 446 | glBindVertexArray(font.vao); 447 | 448 | // update uniforms 449 | glUseProgram(font.program); 450 | glUniform1f(glGetUniformLocation(font.program, "scale_factor"), size/font.font_size); 451 | glUniform2fv(glGetUniformLocation(font.program, "string_offset"), 1, offset); 452 | 453 | GLint dims[4] = {0}; 454 | glGetIntegerv(GL_VIEWPORT, dims); 455 | glUniform2f(glGetUniformLocation(font.program, "resolution"), dims[2], dims[3]); 456 | 457 | 458 | // actual uploading 459 | glBindBuffer(GL_ARRAY_BUFFER, font.vbo_instances); 460 | glBufferSubData(GL_ARRAY_BUFFER, 0, 4*4*ctr, text_glyph_data); 461 | 462 | 463 | // actual drawing 464 | glDrawArraysInstanced(GL_TRIANGLES, 0, 6, ctr); 465 | 466 | // Restore modified GL state 467 | glUseProgram(last_program); 468 | 469 | glActiveTexture(GL_TEXTURE0); 470 | glBindTexture(GL_TEXTURE_2D, last_texture0); 471 | glActiveTexture(GL_TEXTURE1); 472 | glBindTexture(GL_TEXTURE_2D, last_texture1); 473 | glActiveTexture(GL_TEXTURE2); 474 | glBindTexture(GL_TEXTURE_1D, last_texture2); 475 | 476 | glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); 477 | glBindVertexArray(last_vertex_array); 478 | glBlendFunc(last_blend_src, last_blend_dst); 479 | 480 | (last_enable_depth_test ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST)); 481 | (last_enable_blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND)); 482 | } 483 | 484 | 485 | // shader loading routines 486 | char *mv_ef_read_entire_file(const char *filename) { 487 | // Read content of "filename" and return it as a c-string. 488 | printf("Reading %s\n", filename); 489 | FILE *f = fopen(filename, "rb"); 490 | if (!f) { 491 | printf("Error: Could not load shader file!\n"); 492 | return NULL; 493 | } 494 | 495 | fseek(f, 0, SEEK_END); 496 | long fsize = ftell(f); 497 | fseek(f, 0, SEEK_SET); 498 | printf("Filesize = %d\n", (int)fsize); 499 | 500 | char *string = (char*)malloc(fsize + 1); 501 | fread(string, fsize, 1, f); 502 | string[fsize] = '\0'; 503 | fclose(f); 504 | 505 | return string; 506 | } 507 | 508 | // inlined shader source code, so i don't have to ship 2 additional files 509 | // the last two arguments to mv_ef_init() can be used to load custom shader files 510 | char vs_source[] = \ 511 | "#version 330 core\n\ 512 | \n\ 513 | layout(location = 0) in vec2 vertexPosition;\n\ 514 | layout(location = 1) in vec4 instanceGlyph;\n\ 515 | \n\ 516 | uniform sampler2D sampler_font;\n\ 517 | uniform sampler2D sampler_meta;\n\ 518 | \n\ 519 | uniform float offset_firstline; // ascent - descent - linegap/2\n\ 520 | uniform float scale_factor; // scaling factor proportional to font size\n\ 521 | uniform vec2 string_offset; // offset of upper-left corner\n\ 522 | \n\ 523 | uniform vec2 res_meta; // 96x2 \n\ 524 | uniform vec2 res_bitmap; // 512x256\n\ 525 | uniform vec2 resolution; // screen resolution\n\ 526 | \n\ 527 | out vec2 uv;\n\ 528 | out float color_index; // for syntax highlighting\n\ 529 | \n\ 530 | void main()\n\ 531 | {\n\ 532 | // (xoff, yoff, xoff2, yoff2), from second row of texture\n\ 533 | vec4 q2 = texture(sampler_meta, vec2((instanceGlyph.z + 0.5)/res_meta.x, 0.75))*vec4(res_bitmap, res_bitmap);\n\ 534 | \n\ 535 | vec2 p = vertexPosition*(q2.zw - q2.xy) + q2.xy; // offset and scale it properly relative to baseline\n\ 536 | p *= vec2(1.0, -1.0); // flip y, since texture is upside-down\n\ 537 | p.y -= offset_firstline; // make sure the upper-left corner of the string is in the upper-left corner of the screen\n\ 538 | p *= scale_factor; // scale relative to font size\n\ 539 | p += instanceGlyph.xy + string_offset; // move glyph into the right position\n\ 540 | p *= 2.0/resolution; // to NDC\n\ 541 | p += vec2(-1.0, 1.0); // move to upper-left corner instead of center\n\ 542 | \n\ 543 | gl_Position = vec4(p, 0.0, 1.0);\n\ 544 | \n\ 545 | // (x0, y0, x1-x0, y1-y0), from first row of texture\n\ 546 | vec4 q = texture(sampler_meta, vec2((instanceGlyph.z + 0.5)/res_meta.x, 0.25));\n\ 547 | \n\ 548 | // send the correct uv's in the font atlas to the fragment shader\n\ 549 | uv = q.xy + vertexPosition*q.zw;\n\ 550 | color_index = instanceGlyph.w;\n\ 551 | }\n"; 552 | 553 | char fs_source[] = \ 554 | "#version 330 core\n\ 555 | \n\ 556 | in vec2 uv;\n\ 557 | in float color_index;\n\ 558 | \n\ 559 | uniform sampler2D sampler_font;\n\ 560 | uniform sampler1D sampler_colors;\n\ 561 | uniform float num_colors;\n\ 562 | \n\ 563 | out vec4 color;\n\ 564 | \n\ 565 | void main()\n\ 566 | {\n\ 567 | vec3 col = texture(sampler_colors, (color_index+0.5)/num_colors).rgb;\n\ 568 | float s = texture(sampler_font, uv).r;\n\ 569 | color = vec4(col, s);\n\ 570 | }\n"; 571 | 572 | GLuint mv_ef_load_shaders(const char * vertex_file_path,const char * fragment_file_path){ 573 | GLint Result = GL_FALSE; 574 | int InfoLogLength; 575 | 576 | // Create the Vertex shader 577 | GLuint VertexShaderID; 578 | VertexShaderID = glCreateShader(GL_VERTEX_SHADER); 579 | char *VertexShaderCode = vertex_file_path ? mv_ef_read_entire_file(vertex_file_path) : vs_source; 580 | 581 | // Compile Vertex Shader 582 | printf("Compiling shader : %s\n", vertex_file_path); fflush(stdout); 583 | glShaderSource(VertexShaderID, 1, (const char**)&VertexShaderCode , NULL); 584 | glCompileShader(VertexShaderID); 585 | 586 | // Check Vertex Shader 587 | glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result); 588 | glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); 589 | 590 | if ( InfoLogLength > 0 ){ 591 | char VertexShaderErrorMessage[9999]; 592 | glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, VertexShaderErrorMessage); 593 | printf("%s\n", VertexShaderErrorMessage); fflush(stdout); 594 | } 595 | 596 | 597 | // Create the Fragment shader 598 | GLuint FragmentShaderID; 599 | FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); 600 | char *FragmentShaderCode = fragment_file_path ? mv_ef_read_entire_file(fragment_file_path) : fs_source; 601 | 602 | // Compile Fragment Shader 603 | printf("Compiling shader : %s\n", fragment_file_path); fflush(stdout); 604 | glShaderSource(FragmentShaderID, 1, (const char**)&FragmentShaderCode , NULL); 605 | glCompileShader(FragmentShaderID); 606 | 607 | 608 | // Check Fragment Shader 609 | glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result); 610 | glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); 611 | if ( InfoLogLength > 0 ){ 612 | char FragmentShaderErrorMessage[9999]; 613 | glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, FragmentShaderErrorMessage); 614 | printf("%s\n", FragmentShaderErrorMessage); fflush(stdout); 615 | } 616 | 617 | 618 | // Create and Link the program 619 | printf("Linking program\n"); fflush(stdout); 620 | GLuint ProgramID; 621 | ProgramID= glCreateProgram(); 622 | glAttachShader(ProgramID, VertexShaderID); 623 | glAttachShader(ProgramID, FragmentShaderID); 624 | glLinkProgram(ProgramID); 625 | 626 | // Check the program 627 | glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); 628 | glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); 629 | 630 | if ( InfoLogLength > 0 ){ 631 | GLchar ProgramErrorMessage[9999]; 632 | glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]); 633 | printf("%s\n", &ProgramErrorMessage[0]); fflush(stdout); 634 | } 635 | 636 | glDeleteShader(VertexShaderID); 637 | glDeleteShader(FragmentShaderID); 638 | if (fragment_file_path) free(FragmentShaderCode); 639 | if (vertex_file_path) free(VertexShaderCode); 640 | 641 | return ProgramID; 642 | } 643 | 644 | 645 | #endif // defined(MV_EASY_FONT_IMPLEMENTATION) && defined(STB_TRUETYPE_IMPLEMENTATION) 646 | -------------------------------------------------------------------------------- /include/stb_image_write.h: -------------------------------------------------------------------------------- 1 | /* stb_image_write - v1.03 - public domain - http://nothings.org/stb/stb_image_write.h 2 | writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 3 | no warranty implied; use at your own risk 4 | 5 | Before #including, 6 | 7 | #define STB_IMAGE_WRITE_IMPLEMENTATION 8 | 9 | in the file that you want to have the implementation. 10 | 11 | Will probably not work correctly with strict-aliasing optimizations. 12 | 13 | ABOUT: 14 | 15 | This header file is a library for writing images to C stdio. It could be 16 | adapted to write to memory or a general streaming interface; let me know. 17 | 18 | The PNG output is not optimal; it is 20-50% larger than the file 19 | written by a decent optimizing implementation. This library is designed 20 | for source code compactness and simplicity, not optimal image file size 21 | or run-time performance. 22 | 23 | BUILDING: 24 | 25 | You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. 26 | You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace 27 | malloc,realloc,free. 28 | You can define STBIW_MEMMOVE() to replace memmove() 29 | 30 | USAGE: 31 | 32 | There are four functions, one for each image file format: 33 | 34 | int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 35 | int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 36 | int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 37 | int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); 38 | 39 | There are also four equivalent functions that use an arbitrary write function. You are 40 | expected to open/close your file-equivalent before and after calling these: 41 | 42 | int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); 43 | int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 44 | int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 45 | int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); 46 | 47 | where the callback is: 48 | void stbi_write_func(void *context, void *data, int size); 49 | 50 | You can define STBI_WRITE_NO_STDIO to disable the file variant of these 51 | functions, so the library will not use stdio.h at all. However, this will 52 | also disable HDR writing, because it requires stdio for formatted output. 53 | 54 | Each function returns 0 on failure and non-0 on success. 55 | 56 | The functions create an image file defined by the parameters. The image 57 | is a rectangle of pixels stored from left-to-right, top-to-bottom. 58 | Each pixel contains 'comp' channels of data stored interleaved with 8-bits 59 | per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is 60 | monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. 61 | The *data pointer points to the first byte of the top-left-most pixel. 62 | For PNG, "stride_in_bytes" is the distance in bytes from the first byte of 63 | a row of pixels to the first byte of the next row of pixels. 64 | 65 | PNG creates output files with the same number of components as the input. 66 | The BMP format expands Y to RGB in the file format and does not 67 | output alpha. 68 | 69 | PNG supports writing rectangles of data even when the bytes storing rows of 70 | data are not consecutive in memory (e.g. sub-rectangles of a larger image), 71 | by supplying the stride between the beginning of adjacent rows. The other 72 | formats do not. (Thus you cannot write a native-format BMP through the BMP 73 | writer, both because it is in BGR order and because it may have padding 74 | at the end of the line.) 75 | 76 | HDR expects linear float data. Since the format is always 32-bit rgb(e) 77 | data, alpha (if provided) is discarded, and for monochrome data it is 78 | replicated across all three channels. 79 | 80 | TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed 81 | data, set the global variable 'stbi_write_tga_with_rle' to 0. 82 | 83 | CREDITS: 84 | 85 | PNG/BMP/TGA 86 | Sean Barrett 87 | HDR 88 | Baldur Karlsson 89 | TGA monochrome: 90 | Jean-Sebastien Guay 91 | misc enhancements: 92 | Tim Kelsey 93 | TGA RLE 94 | Alan Hickman 95 | initial file IO callback implementation 96 | Emmanuel Julien 97 | bugfixes: 98 | github:Chribba 99 | Guillaume Chereau 100 | github:jry2 101 | github:romigrou 102 | Sergio Gonzalez 103 | Jonas Karlsson 104 | Filip Wasil 105 | Thatcher Ulrich 106 | github:poppolopoppo 107 | 108 | LICENSE 109 | 110 | This software is dual-licensed to the public domain and under the following 111 | license: you are granted a perpetual, irrevocable license to copy, modify, 112 | publish, and distribute this file as you see fit. 113 | 114 | */ 115 | 116 | #ifndef INCLUDE_STB_IMAGE_WRITE_H 117 | #define INCLUDE_STB_IMAGE_WRITE_H 118 | 119 | #ifdef __cplusplus 120 | extern "C" { 121 | #endif 122 | 123 | #ifdef STB_IMAGE_WRITE_STATIC 124 | #define STBIWDEF static 125 | #else 126 | #define STBIWDEF extern 127 | extern int stbi_write_tga_with_rle; 128 | #endif 129 | 130 | #ifndef STBI_WRITE_NO_STDIO 131 | STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 132 | STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 133 | STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 134 | STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); 135 | #endif 136 | 137 | typedef void stbi_write_func(void *context, void *data, int size); 138 | 139 | STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); 140 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 141 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 142 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); 143 | 144 | #ifdef __cplusplus 145 | } 146 | #endif 147 | 148 | #endif//INCLUDE_STB_IMAGE_WRITE_H 149 | 150 | #ifdef STB_IMAGE_WRITE_IMPLEMENTATION 151 | 152 | #ifdef _WIN32 153 | #ifndef _CRT_SECURE_NO_WARNINGS 154 | #define _CRT_SECURE_NO_WARNINGS 155 | #endif 156 | #ifndef _CRT_NONSTDC_NO_DEPRECATE 157 | #define _CRT_NONSTDC_NO_DEPRECATE 158 | #endif 159 | #endif 160 | 161 | #ifndef STBI_WRITE_NO_STDIO 162 | #include 163 | #endif // STBI_WRITE_NO_STDIO 164 | 165 | #include 166 | #include 167 | #include 168 | #include 169 | 170 | #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) 171 | // ok 172 | #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) 173 | // ok 174 | #else 175 | #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." 176 | #endif 177 | 178 | #ifndef STBIW_MALLOC 179 | #define STBIW_MALLOC(sz) malloc(sz) 180 | #define STBIW_REALLOC(p,newsz) realloc(p,newsz) 181 | #define STBIW_FREE(p) free(p) 182 | #endif 183 | 184 | #ifndef STBIW_REALLOC_SIZED 185 | #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) 186 | #endif 187 | 188 | 189 | #ifndef STBIW_MEMMOVE 190 | #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) 191 | #endif 192 | 193 | 194 | #ifndef STBIW_ASSERT 195 | #include 196 | #define STBIW_ASSERT(x) assert(x) 197 | #endif 198 | 199 | #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) 200 | 201 | typedef struct 202 | { 203 | stbi_write_func *func; 204 | void *context; 205 | } stbi__write_context; 206 | 207 | // initialize a callback-based context 208 | static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) 209 | { 210 | s->func = c; 211 | s->context = context; 212 | } 213 | 214 | #ifndef STBI_WRITE_NO_STDIO 215 | 216 | static void stbi__stdio_write(void *context, void *data, int size) 217 | { 218 | fwrite(data,1,size,(FILE*) context); 219 | } 220 | 221 | static int stbi__start_write_file(stbi__write_context *s, const char *filename) 222 | { 223 | FILE *f = fopen(filename, "wb"); 224 | stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); 225 | return f != NULL; 226 | } 227 | 228 | static void stbi__end_write_file(stbi__write_context *s) 229 | { 230 | fclose((FILE *)s->context); 231 | } 232 | 233 | #endif // !STBI_WRITE_NO_STDIO 234 | 235 | typedef unsigned int stbiw_uint32; 236 | typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; 237 | 238 | #ifdef STB_IMAGE_WRITE_STATIC 239 | static int stbi_write_tga_with_rle = 1; 240 | #else 241 | int stbi_write_tga_with_rle = 1; 242 | #endif 243 | 244 | static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) 245 | { 246 | while (*fmt) { 247 | switch (*fmt++) { 248 | case ' ': break; 249 | case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); 250 | s->func(s->context,&x,1); 251 | break; } 252 | case '2': { int x = va_arg(v,int); 253 | unsigned char b[2]; 254 | b[0] = STBIW_UCHAR(x); 255 | b[1] = STBIW_UCHAR(x>>8); 256 | s->func(s->context,b,2); 257 | break; } 258 | case '4': { stbiw_uint32 x = va_arg(v,int); 259 | unsigned char b[4]; 260 | b[0]=STBIW_UCHAR(x); 261 | b[1]=STBIW_UCHAR(x>>8); 262 | b[2]=STBIW_UCHAR(x>>16); 263 | b[3]=STBIW_UCHAR(x>>24); 264 | s->func(s->context,b,4); 265 | break; } 266 | default: 267 | STBIW_ASSERT(0); 268 | return; 269 | } 270 | } 271 | } 272 | 273 | static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) 274 | { 275 | va_list v; 276 | va_start(v, fmt); 277 | stbiw__writefv(s, fmt, v); 278 | va_end(v); 279 | } 280 | 281 | static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) 282 | { 283 | unsigned char arr[3]; 284 | arr[0] = a, arr[1] = b, arr[2] = c; 285 | s->func(s->context, arr, 3); 286 | } 287 | 288 | static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) 289 | { 290 | unsigned char bg[3] = { 255, 0, 255}, px[3]; 291 | int k; 292 | 293 | if (write_alpha < 0) 294 | s->func(s->context, &d[comp - 1], 1); 295 | 296 | switch (comp) { 297 | case 1: 298 | s->func(s->context,d,1); 299 | break; 300 | case 2: 301 | if (expand_mono) 302 | stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp 303 | else 304 | s->func(s->context, d, 1); // monochrome TGA 305 | break; 306 | case 4: 307 | if (!write_alpha) { 308 | // composite against pink background 309 | for (k = 0; k < 3; ++k) 310 | px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; 311 | stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); 312 | break; 313 | } 314 | /* FALLTHROUGH */ 315 | case 3: 316 | stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); 317 | break; 318 | } 319 | if (write_alpha > 0) 320 | s->func(s->context, &d[comp - 1], 1); 321 | } 322 | 323 | static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) 324 | { 325 | stbiw_uint32 zero = 0; 326 | int i,j, j_end; 327 | 328 | if (y <= 0) 329 | return; 330 | 331 | if (vdir < 0) 332 | j_end = -1, j = y-1; 333 | else 334 | j_end = y, j = 0; 335 | 336 | for (; j != j_end; j += vdir) { 337 | for (i=0; i < x; ++i) { 338 | unsigned char *d = (unsigned char *) data + (j*x+i)*comp; 339 | stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); 340 | } 341 | s->func(s->context, &zero, scanline_pad); 342 | } 343 | } 344 | 345 | static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) 346 | { 347 | if (y < 0 || x < 0) { 348 | return 0; 349 | } else { 350 | va_list v; 351 | va_start(v, fmt); 352 | stbiw__writefv(s, fmt, v); 353 | va_end(v); 354 | stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); 355 | return 1; 356 | } 357 | } 358 | 359 | static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) 360 | { 361 | int pad = (-x*3) & 3; 362 | return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, 363 | "11 4 22 4" "4 44 22 444444", 364 | 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 365 | 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header 366 | } 367 | 368 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) 369 | { 370 | stbi__write_context s; 371 | stbi__start_write_callbacks(&s, func, context); 372 | return stbi_write_bmp_core(&s, x, y, comp, data); 373 | } 374 | 375 | #ifndef STBI_WRITE_NO_STDIO 376 | STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) 377 | { 378 | stbi__write_context s; 379 | if (stbi__start_write_file(&s,filename)) { 380 | int r = stbi_write_bmp_core(&s, x, y, comp, data); 381 | stbi__end_write_file(&s); 382 | return r; 383 | } else 384 | return 0; 385 | } 386 | #endif //!STBI_WRITE_NO_STDIO 387 | 388 | static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) 389 | { 390 | int has_alpha = (comp == 2 || comp == 4); 391 | int colorbytes = has_alpha ? comp-1 : comp; 392 | int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 393 | 394 | if (y < 0 || x < 0) 395 | return 0; 396 | 397 | if (!stbi_write_tga_with_rle) { 398 | return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, 399 | "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); 400 | } else { 401 | int i,j,k; 402 | 403 | stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); 404 | 405 | for (j = y - 1; j >= 0; --j) { 406 | unsigned char *row = (unsigned char *) data + j * x * comp; 407 | int len; 408 | 409 | for (i = 0; i < x; i += len) { 410 | unsigned char *begin = row + i * comp; 411 | int diff = 1; 412 | len = 1; 413 | 414 | if (i < x - 1) { 415 | ++len; 416 | diff = memcmp(begin, row + (i + 1) * comp, comp); 417 | if (diff) { 418 | const unsigned char *prev = begin; 419 | for (k = i + 2; k < x && len < 128; ++k) { 420 | if (memcmp(prev, row + k * comp, comp)) { 421 | prev += comp; 422 | ++len; 423 | } else { 424 | --len; 425 | break; 426 | } 427 | } 428 | } else { 429 | for (k = i + 2; k < x && len < 128; ++k) { 430 | if (!memcmp(begin, row + k * comp, comp)) { 431 | ++len; 432 | } else { 433 | break; 434 | } 435 | } 436 | } 437 | } 438 | 439 | if (diff) { 440 | unsigned char header = STBIW_UCHAR(len - 1); 441 | s->func(s->context, &header, 1); 442 | for (k = 0; k < len; ++k) { 443 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); 444 | } 445 | } else { 446 | unsigned char header = STBIW_UCHAR(len - 129); 447 | s->func(s->context, &header, 1); 448 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); 449 | } 450 | } 451 | } 452 | } 453 | return 1; 454 | } 455 | 456 | int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) 457 | { 458 | stbi__write_context s; 459 | stbi__start_write_callbacks(&s, func, context); 460 | return stbi_write_tga_core(&s, x, y, comp, (void *) data); 461 | } 462 | 463 | #ifndef STBI_WRITE_NO_STDIO 464 | int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) 465 | { 466 | stbi__write_context s; 467 | if (stbi__start_write_file(&s,filename)) { 468 | int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); 469 | stbi__end_write_file(&s); 470 | return r; 471 | } else 472 | return 0; 473 | } 474 | #endif 475 | 476 | // ************************************************************************************************* 477 | // Radiance RGBE HDR writer 478 | // by Baldur Karlsson 479 | 480 | #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) 481 | 482 | void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) 483 | { 484 | int exponent; 485 | float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); 486 | 487 | if (maxcomp < 1e-32f) { 488 | rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; 489 | } else { 490 | float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; 491 | 492 | rgbe[0] = (unsigned char)(linear[0] * normalize); 493 | rgbe[1] = (unsigned char)(linear[1] * normalize); 494 | rgbe[2] = (unsigned char)(linear[2] * normalize); 495 | rgbe[3] = (unsigned char)(exponent + 128); 496 | } 497 | } 498 | 499 | void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) 500 | { 501 | unsigned char lengthbyte = STBIW_UCHAR(length+128); 502 | STBIW_ASSERT(length+128 <= 255); 503 | s->func(s->context, &lengthbyte, 1); 504 | s->func(s->context, &databyte, 1); 505 | } 506 | 507 | void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) 508 | { 509 | unsigned char lengthbyte = STBIW_UCHAR(length); 510 | STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code 511 | s->func(s->context, &lengthbyte, 1); 512 | s->func(s->context, data, length); 513 | } 514 | 515 | void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) 516 | { 517 | unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; 518 | unsigned char rgbe[4]; 519 | float linear[3]; 520 | int x; 521 | 522 | scanlineheader[2] = (width&0xff00)>>8; 523 | scanlineheader[3] = (width&0x00ff); 524 | 525 | /* skip RLE for images too small or large */ 526 | if (width < 8 || width >= 32768) { 527 | for (x=0; x < width; x++) { 528 | switch (ncomp) { 529 | case 4: /* fallthrough */ 530 | case 3: linear[2] = scanline[x*ncomp + 2]; 531 | linear[1] = scanline[x*ncomp + 1]; 532 | linear[0] = scanline[x*ncomp + 0]; 533 | break; 534 | default: 535 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; 536 | break; 537 | } 538 | stbiw__linear_to_rgbe(rgbe, linear); 539 | s->func(s->context, rgbe, 4); 540 | } 541 | } else { 542 | int c,r; 543 | /* encode into scratch buffer */ 544 | for (x=0; x < width; x++) { 545 | switch(ncomp) { 546 | case 4: /* fallthrough */ 547 | case 3: linear[2] = scanline[x*ncomp + 2]; 548 | linear[1] = scanline[x*ncomp + 1]; 549 | linear[0] = scanline[x*ncomp + 0]; 550 | break; 551 | default: 552 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; 553 | break; 554 | } 555 | stbiw__linear_to_rgbe(rgbe, linear); 556 | scratch[x + width*0] = rgbe[0]; 557 | scratch[x + width*1] = rgbe[1]; 558 | scratch[x + width*2] = rgbe[2]; 559 | scratch[x + width*3] = rgbe[3]; 560 | } 561 | 562 | s->func(s->context, scanlineheader, 4); 563 | 564 | /* RLE each component separately */ 565 | for (c=0; c < 4; c++) { 566 | unsigned char *comp = &scratch[width*c]; 567 | 568 | x = 0; 569 | while (x < width) { 570 | // find first run 571 | r = x; 572 | while (r+2 < width) { 573 | if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) 574 | break; 575 | ++r; 576 | } 577 | if (r+2 >= width) 578 | r = width; 579 | // dump up to first run 580 | while (x < r) { 581 | int len = r-x; 582 | if (len > 128) len = 128; 583 | stbiw__write_dump_data(s, len, &comp[x]); 584 | x += len; 585 | } 586 | // if there's a run, output it 587 | if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd 588 | // find next byte after run 589 | while (r < width && comp[r] == comp[x]) 590 | ++r; 591 | // output run up to r 592 | while (x < r) { 593 | int len = r-x; 594 | if (len > 127) len = 127; 595 | stbiw__write_run_data(s, len, comp[x]); 596 | x += len; 597 | } 598 | } 599 | } 600 | } 601 | } 602 | } 603 | 604 | static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) 605 | { 606 | if (y <= 0 || x <= 0 || data == NULL) 607 | return 0; 608 | else { 609 | // Each component is stored separately. Allocate scratch space for full output scanline. 610 | unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); 611 | int i, len; 612 | char buffer[128]; 613 | char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; 614 | s->func(s->context, header, sizeof(header)-1); 615 | 616 | len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); 617 | s->func(s->context, buffer, len); 618 | 619 | for(i=0; i < y; i++) 620 | stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); 621 | STBIW_FREE(scratch); 622 | return 1; 623 | } 624 | } 625 | 626 | int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) 627 | { 628 | stbi__write_context s; 629 | stbi__start_write_callbacks(&s, func, context); 630 | return stbi_write_hdr_core(&s, x, y, comp, (float *) data); 631 | } 632 | 633 | #ifndef STBI_WRITE_NO_STDIO 634 | int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) 635 | { 636 | stbi__write_context s; 637 | if (stbi__start_write_file(&s,filename)) { 638 | int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); 639 | stbi__end_write_file(&s); 640 | return r; 641 | } else 642 | return 0; 643 | } 644 | #endif // STBI_WRITE_NO_STDIO 645 | 646 | 647 | ////////////////////////////////////////////////////////////////////////////// 648 | // 649 | // PNG writer 650 | // 651 | 652 | // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() 653 | #define stbiw__sbraw(a) ((int *) (a) - 2) 654 | #define stbiw__sbm(a) stbiw__sbraw(a)[0] 655 | #define stbiw__sbn(a) stbiw__sbraw(a)[1] 656 | 657 | #define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) 658 | #define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) 659 | #define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) 660 | 661 | #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) 662 | #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) 663 | #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) 664 | 665 | static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) 666 | { 667 | int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; 668 | void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); 669 | STBIW_ASSERT(p); 670 | if (p) { 671 | if (!*arr) ((int *) p)[1] = 0; 672 | *arr = (void *) ((int *) p + 2); 673 | stbiw__sbm(*arr) = m; 674 | } 675 | return *arr; 676 | } 677 | 678 | static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) 679 | { 680 | while (*bitcount >= 8) { 681 | stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); 682 | *bitbuffer >>= 8; 683 | *bitcount -= 8; 684 | } 685 | return data; 686 | } 687 | 688 | static int stbiw__zlib_bitrev(int code, int codebits) 689 | { 690 | int res=0; 691 | while (codebits--) { 692 | res = (res << 1) | (code & 1); 693 | code >>= 1; 694 | } 695 | return res; 696 | } 697 | 698 | static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) 699 | { 700 | int i; 701 | for (i=0; i < limit && i < 258; ++i) 702 | if (a[i] != b[i]) break; 703 | return i; 704 | } 705 | 706 | static unsigned int stbiw__zhash(unsigned char *data) 707 | { 708 | stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); 709 | hash ^= hash << 3; 710 | hash += hash >> 5; 711 | hash ^= hash << 4; 712 | hash += hash >> 17; 713 | hash ^= hash << 25; 714 | hash += hash >> 6; 715 | return hash; 716 | } 717 | 718 | #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) 719 | #define stbiw__zlib_add(code,codebits) \ 720 | (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) 721 | #define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) 722 | // default huffman tables 723 | #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) 724 | #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) 725 | #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) 726 | #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) 727 | #define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) 728 | #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) 729 | 730 | #define stbiw__ZHASH 16384 731 | 732 | unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) 733 | { 734 | static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; 735 | static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; 736 | static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; 737 | static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; 738 | unsigned int bitbuf=0; 739 | int i,j, bitcount=0; 740 | unsigned char *out = NULL; 741 | unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); 742 | if (quality < 5) quality = 5; 743 | 744 | stbiw__sbpush(out, 0x78); // DEFLATE 32K window 745 | stbiw__sbpush(out, 0x5e); // FLEVEL = 1 746 | stbiw__zlib_add(1,1); // BFINAL = 1 747 | stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman 748 | 749 | for (i=0; i < stbiw__ZHASH; ++i) 750 | hash_table[i] = NULL; 751 | 752 | i=0; 753 | while (i < data_len-3) { 754 | // hash next 3 bytes of data to be compressed 755 | int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; 756 | unsigned char *bestloc = 0; 757 | unsigned char **hlist = hash_table[h]; 758 | int n = stbiw__sbcount(hlist); 759 | for (j=0; j < n; ++j) { 760 | if (hlist[j]-data > i-32768) { // if entry lies within window 761 | int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); 762 | if (d >= best) best=d,bestloc=hlist[j]; 763 | } 764 | } 765 | // when hash table entry is too long, delete half the entries 766 | if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { 767 | STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); 768 | stbiw__sbn(hash_table[h]) = quality; 769 | } 770 | stbiw__sbpush(hash_table[h],data+i); 771 | 772 | if (bestloc) { 773 | // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal 774 | h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); 775 | hlist = hash_table[h]; 776 | n = stbiw__sbcount(hlist); 777 | for (j=0; j < n; ++j) { 778 | if (hlist[j]-data > i-32767) { 779 | int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); 780 | if (e > best) { // if next match is better, bail on current match 781 | bestloc = NULL; 782 | break; 783 | } 784 | } 785 | } 786 | } 787 | 788 | if (bestloc) { 789 | int d = (int) (data+i - bestloc); // distance back 790 | STBIW_ASSERT(d <= 32767 && best <= 258); 791 | for (j=0; best > lengthc[j+1]-1; ++j); 792 | stbiw__zlib_huff(j+257); 793 | if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); 794 | for (j=0; d > distc[j+1]-1; ++j); 795 | stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); 796 | if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); 797 | i += best; 798 | } else { 799 | stbiw__zlib_huffb(data[i]); 800 | ++i; 801 | } 802 | } 803 | // write out final bytes 804 | for (;i < data_len; ++i) 805 | stbiw__zlib_huffb(data[i]); 806 | stbiw__zlib_huff(256); // end of block 807 | // pad with 0 bits to byte boundary 808 | while (bitcount) 809 | stbiw__zlib_add(0,1); 810 | 811 | for (i=0; i < stbiw__ZHASH; ++i) 812 | (void) stbiw__sbfree(hash_table[i]); 813 | STBIW_FREE(hash_table); 814 | 815 | { 816 | // compute adler32 on input 817 | unsigned int s1=1, s2=0; 818 | int blocklen = (int) (data_len % 5552); 819 | j=0; 820 | while (j < data_len) { 821 | for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; 822 | s1 %= 65521, s2 %= 65521; 823 | j += blocklen; 824 | blocklen = 5552; 825 | } 826 | stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); 827 | stbiw__sbpush(out, STBIW_UCHAR(s2)); 828 | stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); 829 | stbiw__sbpush(out, STBIW_UCHAR(s1)); 830 | } 831 | *out_len = stbiw__sbn(out); 832 | // make returned pointer freeable 833 | STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); 834 | return (unsigned char *) stbiw__sbraw(out); 835 | } 836 | 837 | static unsigned int stbiw__crc32(unsigned char *buffer, int len) 838 | { 839 | static unsigned int crc_table[256] = 840 | { 841 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 842 | 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 843 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 844 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 845 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 846 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 847 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 848 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 849 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 850 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 851 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 852 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 853 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 854 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 855 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 856 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 857 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 858 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 859 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 860 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 861 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 862 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 863 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 864 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 865 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 866 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 867 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 868 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 869 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 870 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 871 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 872 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D 873 | }; 874 | 875 | unsigned int crc = ~0u; 876 | int i; 877 | for (i=0; i < len; ++i) 878 | crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; 879 | return ~crc; 880 | } 881 | 882 | #define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) 883 | #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); 884 | #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) 885 | 886 | static void stbiw__wpcrc(unsigned char **data, int len) 887 | { 888 | unsigned int crc = stbiw__crc32(*data - len - 4, len+4); 889 | stbiw__wp32(*data, crc); 890 | } 891 | 892 | static unsigned char stbiw__paeth(int a, int b, int c) 893 | { 894 | int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); 895 | if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); 896 | if (pb <= pc) return STBIW_UCHAR(b); 897 | return STBIW_UCHAR(c); 898 | } 899 | 900 | unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) 901 | { 902 | int ctype[5] = { -1, 0, 4, 2, 6 }; 903 | unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; 904 | unsigned char *out,*o, *filt, *zlib; 905 | signed char *line_buffer; 906 | int i,j,k,p,zlen; 907 | 908 | if (stride_bytes == 0) 909 | stride_bytes = x * n; 910 | 911 | filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; 912 | line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } 913 | for (j=0; j < y; ++j) { 914 | static int mapping[] = { 0,1,2,3,4 }; 915 | static int firstmap[] = { 0,1,0,5,6 }; 916 | int *mymap = j ? mapping : firstmap; 917 | int best = 0, bestval = 0x7fffffff; 918 | for (p=0; p < 2; ++p) { 919 | for (k= p?best:0; k < 5; ++k) { 920 | int type = mymap[k],est=0; 921 | unsigned char *z = pixels + stride_bytes*j; 922 | for (i=0; i < n; ++i) 923 | switch (type) { 924 | case 0: line_buffer[i] = z[i]; break; 925 | case 1: line_buffer[i] = z[i]; break; 926 | case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; 927 | case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; 928 | case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; 929 | case 5: line_buffer[i] = z[i]; break; 930 | case 6: line_buffer[i] = z[i]; break; 931 | } 932 | for (i=n; i < x*n; ++i) { 933 | switch (type) { 934 | case 0: line_buffer[i] = z[i]; break; 935 | case 1: line_buffer[i] = z[i] - z[i-n]; break; 936 | case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; 937 | case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; 938 | case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; 939 | case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; 940 | case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; 941 | } 942 | } 943 | if (p) break; 944 | for (i=0; i < x*n; ++i) 945 | est += abs((signed char) line_buffer[i]); 946 | if (est < bestval) { bestval = est; best = k; } 947 | } 948 | } 949 | // when we get here, best contains the filter type, and line_buffer contains the data 950 | filt[j*(x*n+1)] = (unsigned char) best; 951 | STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); 952 | } 953 | STBIW_FREE(line_buffer); 954 | zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory 955 | STBIW_FREE(filt); 956 | if (!zlib) return 0; 957 | 958 | // each tag requires 12 bytes of overhead 959 | out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); 960 | if (!out) return 0; 961 | *out_len = 8 + 12+13 + 12+zlen + 12; 962 | 963 | o=out; 964 | STBIW_MEMMOVE(o,sig,8); o+= 8; 965 | stbiw__wp32(o, 13); // header length 966 | stbiw__wptag(o, "IHDR"); 967 | stbiw__wp32(o, x); 968 | stbiw__wp32(o, y); 969 | *o++ = 8; 970 | *o++ = STBIW_UCHAR(ctype[n]); 971 | *o++ = 0; 972 | *o++ = 0; 973 | *o++ = 0; 974 | stbiw__wpcrc(&o,13); 975 | 976 | stbiw__wp32(o, zlen); 977 | stbiw__wptag(o, "IDAT"); 978 | STBIW_MEMMOVE(o, zlib, zlen); 979 | o += zlen; 980 | STBIW_FREE(zlib); 981 | stbiw__wpcrc(&o, zlen); 982 | 983 | stbiw__wp32(o,0); 984 | stbiw__wptag(o, "IEND"); 985 | stbiw__wpcrc(&o,0); 986 | 987 | STBIW_ASSERT(o == out + *out_len); 988 | 989 | return out; 990 | } 991 | 992 | #ifndef STBI_WRITE_NO_STDIO 993 | STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) 994 | { 995 | FILE *f; 996 | int len; 997 | unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); 998 | if (png == NULL) return 0; 999 | f = fopen(filename, "wb"); 1000 | if (!f) { STBIW_FREE(png); return 0; } 1001 | fwrite(png, 1, len, f); 1002 | fclose(f); 1003 | STBIW_FREE(png); 1004 | return 1; 1005 | } 1006 | #endif 1007 | 1008 | STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) 1009 | { 1010 | int len; 1011 | unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); 1012 | if (png == NULL) return 0; 1013 | func(context, png, len); 1014 | STBIW_FREE(png); 1015 | return 1; 1016 | } 1017 | 1018 | #endif // STB_IMAGE_WRITE_IMPLEMENTATION 1019 | 1020 | /* Revision history 1021 | 1.02 (2016-04-02) 1022 | avoid allocating large structures on the stack 1023 | 1.01 (2016-01-16) 1024 | STBIW_REALLOC_SIZED: support allocators with no realloc support 1025 | avoid race-condition in crc initialization 1026 | minor compile issues 1027 | 1.00 (2015-09-14) 1028 | installable file IO function 1029 | 0.99 (2015-09-13) 1030 | warning fixes; TGA rle support 1031 | 0.98 (2015-04-08) 1032 | added STBIW_MALLOC, STBIW_ASSERT etc 1033 | 0.97 (2015-01-18) 1034 | fixed HDR asserts, rewrote HDR rle logic 1035 | 0.96 (2015-01-17) 1036 | add HDR output 1037 | fix monochrome BMP 1038 | 0.95 (2014-08-17) 1039 | add monochrome TGA output 1040 | 0.94 (2014-05-31) 1041 | rename private functions to avoid conflicts with stb_image.h 1042 | 0.93 (2014-05-27) 1043 | warning fixes 1044 | 0.92 (2010-08-01) 1045 | casts to unsigned char to fix warnings 1046 | 0.91 (2010-07-17) 1047 | first public release 1048 | 0.90 first internal release 1049 | */ 1050 | -------------------------------------------------------------------------------- /include/stb_rect_pack.h: -------------------------------------------------------------------------------- 1 | // stb_rect_pack.h - v0.10 - public domain - rectangle packing 2 | // Sean Barrett 2014 3 | // 4 | // Useful for e.g. packing rectangular textures into an atlas. 5 | // Does not do rotation. 6 | // 7 | // Not necessarily the awesomest packing method, but better than 8 | // the totally naive one in stb_truetype (which is primarily what 9 | // this is meant to replace). 10 | // 11 | // Has only had a few tests run, may have issues. 12 | // 13 | // More docs to come. 14 | // 15 | // No memory allocations; uses qsort() and assert() from stdlib. 16 | // Can override those by defining STBRP_SORT and STBRP_ASSERT. 17 | // 18 | // This library currently uses the Skyline Bottom-Left algorithm. 19 | // 20 | // Please note: better rectangle packers are welcome! Please 21 | // implement them to the same API, but with a different init 22 | // function. 23 | // 24 | // Credits 25 | // 26 | // Library 27 | // Sean Barrett 28 | // Minor features 29 | // Martins Mozeiko 30 | // Bugfixes / warning fixes 31 | // Jeremy Jaussaud 32 | // 33 | // Version history: 34 | // 35 | // 0.10 (2016-10-25) remove cast-away-const to avoid warnings 36 | // 0.09 (2016-08-27) fix compiler warnings 37 | // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) 38 | // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) 39 | // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort 40 | // 0.05: added STBRP_ASSERT to allow replacing assert 41 | // 0.04: fixed minor bug in STBRP_LARGE_RECTS support 42 | // 0.01: initial release 43 | // 44 | // LICENSE 45 | // 46 | // This software is dual-licensed to the public domain and under the following 47 | // license: you are granted a perpetual, irrevocable license to copy, modify, 48 | // publish, and distribute this file as you see fit. 49 | 50 | ////////////////////////////////////////////////////////////////////////////// 51 | // 52 | // INCLUDE SECTION 53 | // 54 | 55 | #ifndef STB_INCLUDE_STB_RECT_PACK_H 56 | #define STB_INCLUDE_STB_RECT_PACK_H 57 | 58 | #define STB_RECT_PACK_VERSION 1 59 | 60 | #ifdef STBRP_STATIC 61 | #define STBRP_DEF static 62 | #else 63 | #define STBRP_DEF extern 64 | #endif 65 | 66 | #ifdef __cplusplus 67 | extern "C" { 68 | #endif 69 | 70 | typedef struct stbrp_context stbrp_context; 71 | typedef struct stbrp_node stbrp_node; 72 | typedef struct stbrp_rect stbrp_rect; 73 | 74 | #ifdef STBRP_LARGE_RECTS 75 | typedef int stbrp_coord; 76 | #else 77 | typedef unsigned short stbrp_coord; 78 | #endif 79 | 80 | STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); 81 | // Assign packed locations to rectangles. The rectangles are of type 82 | // 'stbrp_rect' defined below, stored in the array 'rects', and there 83 | // are 'num_rects' many of them. 84 | // 85 | // Rectangles which are successfully packed have the 'was_packed' flag 86 | // set to a non-zero value and 'x' and 'y' store the minimum location 87 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left 88 | // if you imagine y increasing downwards). Rectangles which do not fit 89 | // have the 'was_packed' flag set to 0. 90 | // 91 | // You should not try to access the 'rects' array from another thread 92 | // while this function is running, as the function temporarily reorders 93 | // the array while it executes. 94 | // 95 | // To pack into another rectangle, you need to call stbrp_init_target 96 | // again. To continue packing into the same rectangle, you can call 97 | // this function again. Calling this multiple times with multiple rect 98 | // arrays will probably produce worse packing results than calling it 99 | // a single time with the full rectangle array, but the option is 100 | // available. 101 | 102 | struct stbrp_rect 103 | { 104 | // reserved for your use: 105 | int id; 106 | 107 | // input: 108 | stbrp_coord w, h; 109 | 110 | // output: 111 | stbrp_coord x, y; 112 | int was_packed; // non-zero if valid packing 113 | 114 | }; // 16 bytes, nominally 115 | 116 | 117 | STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); 118 | // Initialize a rectangle packer to: 119 | // pack a rectangle that is 'width' by 'height' in dimensions 120 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long 121 | // 122 | // You must call this function every time you start packing into a new target. 123 | // 124 | // There is no "shutdown" function. The 'nodes' memory must stay valid for 125 | // the following stbrp_pack_rects() call (or calls), but can be freed after 126 | // the call (or calls) finish. 127 | // 128 | // Note: to guarantee best results, either: 129 | // 1. make sure 'num_nodes' >= 'width' 130 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 131 | // 132 | // If you don't do either of the above things, widths will be quantized to multiples 133 | // of small integers to guarantee the algorithm doesn't run out of temporary storage. 134 | // 135 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm 136 | // may run out of temporary storage and be unable to pack some rectangles. 137 | 138 | STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); 139 | // Optionally call this function after init but before doing any packing to 140 | // change the handling of the out-of-temp-memory scenario, described above. 141 | // If you call init again, this will be reset to the default (false). 142 | 143 | 144 | STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); 145 | // Optionally select which packing heuristic the library should use. Different 146 | // heuristics will produce better/worse results for different data sets. 147 | // If you call init again, this will be reset to the default. 148 | 149 | enum 150 | { 151 | STBRP_HEURISTIC_Skyline_default=0, 152 | STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, 153 | STBRP_HEURISTIC_Skyline_BF_sortHeight 154 | }; 155 | 156 | 157 | ////////////////////////////////////////////////////////////////////////////// 158 | // 159 | // the details of the following structures don't matter to you, but they must 160 | // be visible so you can handle the memory allocations for them 161 | 162 | struct stbrp_node 163 | { 164 | stbrp_coord x,y; 165 | stbrp_node *next; 166 | }; 167 | 168 | struct stbrp_context 169 | { 170 | int width; 171 | int height; 172 | int align; 173 | int init_mode; 174 | int heuristic; 175 | int num_nodes; 176 | stbrp_node *active_head; 177 | stbrp_node *free_head; 178 | stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 179 | }; 180 | 181 | #ifdef __cplusplus 182 | } 183 | #endif 184 | 185 | #endif 186 | 187 | ////////////////////////////////////////////////////////////////////////////// 188 | // 189 | // IMPLEMENTATION SECTION 190 | // 191 | 192 | #ifdef STB_RECT_PACK_IMPLEMENTATION 193 | #ifndef STBRP_SORT 194 | #include 195 | #define STBRP_SORT qsort 196 | #endif 197 | 198 | #ifndef STBRP_ASSERT 199 | #include 200 | #define STBRP_ASSERT assert 201 | #endif 202 | 203 | #ifdef _MSC_VER 204 | #define STBRP__NOTUSED(v) (void)(v) 205 | #else 206 | #define STBRP__NOTUSED(v) (void)sizeof(v) 207 | #endif 208 | 209 | enum 210 | { 211 | STBRP__INIT_skyline = 1 212 | }; 213 | 214 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) 215 | { 216 | switch (context->init_mode) { 217 | case STBRP__INIT_skyline: 218 | STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); 219 | context->heuristic = heuristic; 220 | break; 221 | default: 222 | STBRP_ASSERT(0); 223 | } 224 | } 225 | 226 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) 227 | { 228 | if (allow_out_of_mem) 229 | // if it's ok to run out of memory, then don't bother aligning them; 230 | // this gives better packing, but may fail due to OOM (even though 231 | // the rectangles easily fit). @TODO a smarter approach would be to only 232 | // quantize once we've hit OOM, then we could get rid of this parameter. 233 | context->align = 1; 234 | else { 235 | // if it's not ok to run out of memory, then quantize the widths 236 | // so that num_nodes is always enough nodes. 237 | // 238 | // I.e. num_nodes * align >= width 239 | // align >= width / num_nodes 240 | // align = ceil(width/num_nodes) 241 | 242 | context->align = (context->width + context->num_nodes-1) / context->num_nodes; 243 | } 244 | } 245 | 246 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) 247 | { 248 | int i; 249 | #ifndef STBRP_LARGE_RECTS 250 | STBRP_ASSERT(width <= 0xffff && height <= 0xffff); 251 | #endif 252 | 253 | for (i=0; i < num_nodes-1; ++i) 254 | nodes[i].next = &nodes[i+1]; 255 | nodes[i].next = NULL; 256 | context->init_mode = STBRP__INIT_skyline; 257 | context->heuristic = STBRP_HEURISTIC_Skyline_default; 258 | context->free_head = &nodes[0]; 259 | context->active_head = &context->extra[0]; 260 | context->width = width; 261 | context->height = height; 262 | context->num_nodes = num_nodes; 263 | stbrp_setup_allow_out_of_mem(context, 0); 264 | 265 | // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) 266 | context->extra[0].x = 0; 267 | context->extra[0].y = 0; 268 | context->extra[0].next = &context->extra[1]; 269 | context->extra[1].x = (stbrp_coord) width; 270 | #ifdef STBRP_LARGE_RECTS 271 | context->extra[1].y = (1<<30); 272 | #else 273 | context->extra[1].y = 65535; 274 | #endif 275 | context->extra[1].next = NULL; 276 | } 277 | 278 | // find minimum y position if it starts at x1 279 | static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) 280 | { 281 | stbrp_node *node = first; 282 | int x1 = x0 + width; 283 | int min_y, visited_width, waste_area; 284 | 285 | STBRP__NOTUSED(c); 286 | 287 | STBRP_ASSERT(first->x <= x0); 288 | 289 | #if 0 290 | // skip in case we're past the node 291 | while (node->next->x <= x0) 292 | ++node; 293 | #else 294 | STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency 295 | #endif 296 | 297 | STBRP_ASSERT(node->x <= x0); 298 | 299 | min_y = 0; 300 | waste_area = 0; 301 | visited_width = 0; 302 | while (node->x < x1) { 303 | if (node->y > min_y) { 304 | // raise min_y higher. 305 | // we've accounted for all waste up to min_y, 306 | // but we'll now add more waste for everything we've visted 307 | waste_area += visited_width * (node->y - min_y); 308 | min_y = node->y; 309 | // the first time through, visited_width might be reduced 310 | if (node->x < x0) 311 | visited_width += node->next->x - x0; 312 | else 313 | visited_width += node->next->x - node->x; 314 | } else { 315 | // add waste area 316 | int under_width = node->next->x - node->x; 317 | if (under_width + visited_width > width) 318 | under_width = width - visited_width; 319 | waste_area += under_width * (min_y - node->y); 320 | visited_width += under_width; 321 | } 322 | node = node->next; 323 | } 324 | 325 | *pwaste = waste_area; 326 | return min_y; 327 | } 328 | 329 | typedef struct 330 | { 331 | int x,y; 332 | stbrp_node **prev_link; 333 | } stbrp__findresult; 334 | 335 | static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) 336 | { 337 | int best_waste = (1<<30), best_x, best_y = (1 << 30); 338 | stbrp__findresult fr; 339 | stbrp_node **prev, *node, *tail, **best = NULL; 340 | 341 | // align to multiple of c->align 342 | width = (width + c->align - 1); 343 | width -= width % c->align; 344 | STBRP_ASSERT(width % c->align == 0); 345 | 346 | node = c->active_head; 347 | prev = &c->active_head; 348 | while (node->x + width <= c->width) { 349 | int y,waste; 350 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 351 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL 352 | // bottom left 353 | if (y < best_y) { 354 | best_y = y; 355 | best = prev; 356 | } 357 | } else { 358 | // best-fit 359 | if (y + height <= c->height) { 360 | // can only use it if it first vertically 361 | if (y < best_y || (y == best_y && waste < best_waste)) { 362 | best_y = y; 363 | best_waste = waste; 364 | best = prev; 365 | } 366 | } 367 | } 368 | prev = &node->next; 369 | node = node->next; 370 | } 371 | 372 | best_x = (best == NULL) ? 0 : (*best)->x; 373 | 374 | // if doing best-fit (BF), we also have to try aligning right edge to each node position 375 | // 376 | // e.g, if fitting 377 | // 378 | // ____________________ 379 | // |____________________| 380 | // 381 | // into 382 | // 383 | // | | 384 | // | ____________| 385 | // |____________| 386 | // 387 | // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned 388 | // 389 | // This makes BF take about 2x the time 390 | 391 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { 392 | tail = c->active_head; 393 | node = c->active_head; 394 | prev = &c->active_head; 395 | // find first node that's admissible 396 | while (tail->x < width) 397 | tail = tail->next; 398 | while (tail) { 399 | int xpos = tail->x - width; 400 | int y,waste; 401 | STBRP_ASSERT(xpos >= 0); 402 | // find the left position that matches this 403 | while (node->next->x <= xpos) { 404 | prev = &node->next; 405 | node = node->next; 406 | } 407 | STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); 408 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 409 | if (y + height < c->height) { 410 | if (y <= best_y) { 411 | if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { 412 | best_x = xpos; 413 | STBRP_ASSERT(y <= best_y); 414 | best_y = y; 415 | best_waste = waste; 416 | best = prev; 417 | } 418 | } 419 | } 420 | tail = tail->next; 421 | } 422 | } 423 | 424 | fr.prev_link = best; 425 | fr.x = best_x; 426 | fr.y = best_y; 427 | return fr; 428 | } 429 | 430 | static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) 431 | { 432 | // find best position according to heuristic 433 | stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); 434 | stbrp_node *node, *cur; 435 | 436 | // bail if: 437 | // 1. it failed 438 | // 2. the best node doesn't fit (we don't always check this) 439 | // 3. we're out of memory 440 | if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { 441 | res.prev_link = NULL; 442 | return res; 443 | } 444 | 445 | // on success, create new node 446 | node = context->free_head; 447 | node->x = (stbrp_coord) res.x; 448 | node->y = (stbrp_coord) (res.y + height); 449 | 450 | context->free_head = node->next; 451 | 452 | // insert the new node into the right starting point, and 453 | // let 'cur' point to the remaining nodes needing to be 454 | // stiched back in 455 | 456 | cur = *res.prev_link; 457 | if (cur->x < res.x) { 458 | // preserve the existing one, so start testing with the next one 459 | stbrp_node *next = cur->next; 460 | cur->next = node; 461 | cur = next; 462 | } else { 463 | *res.prev_link = node; 464 | } 465 | 466 | // from here, traverse cur and free the nodes, until we get to one 467 | // that shouldn't be freed 468 | while (cur->next && cur->next->x <= res.x + width) { 469 | stbrp_node *next = cur->next; 470 | // move the current node to the free list 471 | cur->next = context->free_head; 472 | context->free_head = cur; 473 | cur = next; 474 | } 475 | 476 | // stitch the list back in 477 | node->next = cur; 478 | 479 | if (cur->x < res.x + width) 480 | cur->x = (stbrp_coord) (res.x + width); 481 | 482 | #ifdef _DEBUG 483 | cur = context->active_head; 484 | while (cur->x < context->width) { 485 | STBRP_ASSERT(cur->x < cur->next->x); 486 | cur = cur->next; 487 | } 488 | STBRP_ASSERT(cur->next == NULL); 489 | 490 | { 491 | stbrp_node *L1 = NULL, *L2 = NULL; 492 | int count=0; 493 | cur = context->active_head; 494 | while (cur) { 495 | L1 = cur; 496 | cur = cur->next; 497 | ++count; 498 | } 499 | cur = context->free_head; 500 | while (cur) { 501 | L2 = cur; 502 | cur = cur->next; 503 | ++count; 504 | } 505 | STBRP_ASSERT(count == context->num_nodes+2); 506 | } 507 | #endif 508 | 509 | return res; 510 | } 511 | 512 | static int rect_height_compare(const void *a, const void *b) 513 | { 514 | const stbrp_rect *p = (const stbrp_rect *) a; 515 | const stbrp_rect *q = (const stbrp_rect *) b; 516 | if (p->h > q->h) 517 | return -1; 518 | if (p->h < q->h) 519 | return 1; 520 | return (p->w > q->w) ? -1 : (p->w < q->w); 521 | } 522 | 523 | static int rect_width_compare(const void *a, const void *b) 524 | { 525 | const stbrp_rect *p = (const stbrp_rect *) a; 526 | const stbrp_rect *q = (const stbrp_rect *) b; 527 | if (p->w > q->w) 528 | return -1; 529 | if (p->w < q->w) 530 | return 1; 531 | return (p->h > q->h) ? -1 : (p->h < q->h); 532 | } 533 | 534 | static int rect_original_order(const void *a, const void *b) 535 | { 536 | const stbrp_rect *p = (const stbrp_rect *) a; 537 | const stbrp_rect *q = (const stbrp_rect *) b; 538 | return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); 539 | } 540 | 541 | #ifdef STBRP_LARGE_RECTS 542 | #define STBRP__MAXVAL 0xffffffff 543 | #else 544 | #define STBRP__MAXVAL 0xffff 545 | #endif 546 | 547 | STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) 548 | { 549 | int i; 550 | 551 | // we use the 'was_packed' field internally to allow sorting/unsorting 552 | for (i=0; i < num_rects; ++i) { 553 | rects[i].was_packed = i; 554 | #ifndef STBRP_LARGE_RECTS 555 | STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); 556 | #endif 557 | } 558 | 559 | // sort according to heuristic 560 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); 561 | 562 | for (i=0; i < num_rects; ++i) { 563 | if (rects[i].w == 0 || rects[i].h == 0) { 564 | rects[i].x = rects[i].y = 0; // empty rect needs no space 565 | } else { 566 | stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 567 | if (fr.prev_link) { 568 | rects[i].x = (stbrp_coord) fr.x; 569 | rects[i].y = (stbrp_coord) fr.y; 570 | } else { 571 | rects[i].x = rects[i].y = STBRP__MAXVAL; 572 | } 573 | } 574 | } 575 | 576 | // unsort 577 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); 578 | 579 | // set was_packed flags 580 | for (i=0; i < num_rects; ++i) 581 | rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); 582 | } 583 | #endif 584 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "stb_truetype.h" 11 | #include "mv_easy_font.h" 12 | 13 | /* 14 | Uniform random numbers between 0.0 (inclusive) and 1.0 (exclusive) 15 | Lehmer RNG, "minimal standard" 16 | */ 17 | double rng() 18 | { 19 | static unsigned int seed = 123; 20 | seed *= 16807; 21 | return seed / (double)0x100000000ULL; 22 | } 23 | 24 | GLFWwindow *window; 25 | double resx = 1600; 26 | double resy = 1000; 27 | 28 | double prevx, prevy; // for mouse position 29 | int clickedButtons = 0; // bit field for mouse clicks 30 | 31 | int SKIP_DRAWING = 0; 32 | 33 | enum buttonMaps { FIRST_BUTTON=1, SECOND_BUTTON=2, THIRD_BUTTON=4, FOURTH_BUTTON=8, FIFTH_BUTTON=16, NO_BUTTON=0 }; 34 | enum modifierMaps { CTRL=2, SHIFT=1, ALT=4, META=8, NO_MODIFIER=0 }; 35 | 36 | // all glfw and opengl init here 37 | void init_GL(); 38 | void frame_timer(); 39 | 40 | // callback functions to send to glfw 41 | void key_callback(GLFWwindow* win, int key, int scancode, int action, int mods); 42 | void mousebutton_callback(GLFWwindow* win, int button, int action, int mods); 43 | void mousepos_callback(GLFWwindow* win, double xpos, double ypos); 44 | void mousewheel_callback(GLFWwindow* win, double xoffset, double yoffset); 45 | void windowsize_callback(GLFWwindow *win, int width, int height); 46 | 47 | 48 | typedef enum Token_Type {TOKEN_OTHER=0, TOKEN_OPERATOR, TOKEN_NUMERIC, TOKEN_FUNCTION, TOKEN_KEYWORD, TOKEN_COMMENT, TOKEN_VARIABLE, TOKEN_UNSET} Token_Type; 49 | 50 | const char *TOKEN_NAMES[] = {"other", "operator", "numeric", "function", "keyword", "comment", "type", "unset"}; 51 | const char *TYPES[] = {"void", "int", "float", "vec2", "vec3", "vec4", "sampler1D", "sampler2D"}; 52 | const char *KEYWORDS[] = {"#version", "#define", "in", "out", "uniform", "layout", "return", "if", "else", "for", "while"}; 53 | 54 | typedef struct Token 55 | { 56 | char *start; 57 | char *stop; 58 | Token_Type type; 59 | } Token; 60 | 61 | 62 | // syntax highlighter 63 | void color_string(char *str, char *col) 64 | { 65 | // ignored characters 66 | char delims[] = " ,(){}[];\t\n"; 67 | int num_delims = strlen(delims); 68 | 69 | char operators[] = "/+-*<>=&|"; 70 | int num_operators = strlen(operators); 71 | 72 | Token tokens[9999]; // hurr 73 | int num_tokens = 0; // running counter 74 | 75 | char *ptr = str; 76 | while (*ptr) { 77 | // skip delimiters 78 | int is_delim = 0; 79 | for (int i = 0; i < num_delims; i++) { 80 | if (*ptr == delims[i]) { 81 | is_delim = 1; 82 | break; 83 | } 84 | } 85 | 86 | if (is_delim == 1) { 87 | ptr++; 88 | continue; 89 | } 90 | 91 | // found a token! 92 | char *start = ptr; 93 | 94 | if (*ptr == '/' && *(ptr+1) == '/') { 95 | // found a line comment, go to end of line or end of file 96 | while (*ptr != '\n' && *ptr != '\0') { 97 | ptr++; 98 | } 99 | 100 | tokens[num_tokens].start = start; 101 | tokens[num_tokens].stop = ptr; 102 | tokens[num_tokens].type = TOKEN_COMMENT; 103 | num_tokens++; 104 | 105 | ptr++; 106 | continue; 107 | } 108 | 109 | if (*ptr == '/' && *(ptr+1) == '*') { 110 | // found a block comment, go to end of line or end of file 111 | while (!(*ptr == '*' && *(ptr+1) == '/') && *ptr != '\0') { 112 | ptr++; 113 | } 114 | ptr++; 115 | 116 | tokens[num_tokens].start = start; 117 | tokens[num_tokens].stop = ptr+1; 118 | tokens[num_tokens].type = TOKEN_COMMENT; 119 | num_tokens++; 120 | 121 | ptr++; 122 | continue; 123 | } 124 | 125 | // check if it's an operator 126 | int is_operator = 0; 127 | for (int i = 0; i < num_operators; i++) { 128 | if (*ptr == operators[i]) { 129 | is_operator = 1; 130 | break; 131 | } 132 | } 133 | 134 | if (is_operator == 1) { 135 | tokens[num_tokens].start = start; 136 | tokens[num_tokens].stop = ptr+1; 137 | tokens[num_tokens].type = TOKEN_OPERATOR; 138 | num_tokens++; 139 | ptr++; 140 | continue; 141 | } 142 | 143 | // it's either a name, type, a keyword, a function, or an names separated by an operator without spaces 144 | while (*ptr) { 145 | // check whether it's an operator stuck between two names 146 | int is_operator2 = 0; 147 | for (int i = 0; i < num_operators; i++) { 148 | if (*ptr == operators[i]) { 149 | is_operator2 = 1; 150 | break; 151 | } 152 | } 153 | 154 | if (is_operator2 == 1) { 155 | tokens[num_tokens].start = start; 156 | tokens[num_tokens].stop = ptr; 157 | tokens[num_tokens].type = TOKEN_UNSET; 158 | num_tokens++; 159 | break; 160 | } 161 | 162 | // otherwise go until we find the next delimiter 163 | int is_delim2 = 0; 164 | for (int i = 0; i < num_delims; i++) { 165 | if (*ptr == delims[i]) { 166 | is_delim2 = 1; 167 | break; 168 | } 169 | } 170 | 171 | if (is_delim2 == 1) { 172 | tokens[num_tokens].start = start; 173 | tokens[num_tokens].stop = ptr; 174 | tokens[num_tokens].type = TOKEN_UNSET; 175 | num_tokens++; 176 | ptr++; 177 | break; 178 | } 179 | 180 | // did not find delimiter, check next char 181 | ptr++; 182 | } 183 | } 184 | 185 | // determine the types of the unset tokens, i.e. either 186 | // a name, a type, a keyword, or a function 187 | int num_keywords = sizeof(KEYWORDS)/sizeof(char*); 188 | int num_types = sizeof(TYPES)/sizeof(char*); 189 | 190 | for (int i = 0; i < num_tokens; i++) { 191 | // TOKEN_OPERATOR and TOKEN_COMMENT should already be set, so skip those 192 | if (tokens[i].type != TOKEN_UNSET) { 193 | continue; 194 | } 195 | 196 | char end_char = *tokens[i].stop; 197 | 198 | // temporarily null terminate at end of token, restored after parsing 199 | *tokens[i].stop = '\0'; 200 | 201 | // parse 202 | 203 | // if it's a keyword 204 | int is_keyword = 0; 205 | for (int j = 0; j < num_keywords; j++) { 206 | if (strcmp(tokens[i].start, KEYWORDS[j]) == 0) { 207 | is_keyword = 1; 208 | break; 209 | } 210 | } 211 | if (is_keyword == 1) { 212 | tokens[i].type = TOKEN_KEYWORD; 213 | *tokens[i].stop = end_char; 214 | continue; 215 | } 216 | 217 | // Check if it's a function 218 | float f; 219 | if (end_char == '(') { 220 | tokens[i].type = TOKEN_FUNCTION; 221 | *tokens[i].stop = end_char; 222 | continue; 223 | } 224 | 225 | // or if it's a numeric value. catches both integers and floats 226 | if (sscanf(tokens[i].start, "%f", &f) == 1) { 227 | tokens[i].type = TOKEN_NUMERIC; 228 | *tokens[i].stop = end_char; 229 | continue; 230 | } 231 | 232 | // if it's a variable type 233 | int is_type = 0; 234 | for (int j = 0; j < num_types; j++) { 235 | if (strcmp(tokens[i].start, TYPES[j]) == 0) { 236 | is_type = 1; 237 | break; 238 | } 239 | } 240 | if (is_type == 1) { 241 | tokens[i].type = TOKEN_VARIABLE; 242 | *tokens[i].stop = end_char; 243 | continue; 244 | } 245 | 246 | // otherwise it's a regular variable name 247 | tokens[i].type = TOKEN_OTHER; 248 | *tokens[i].stop = end_char; 249 | } 250 | 251 | // print all tokens and their types 252 | for (int i = 0; i < num_tokens; i++) { 253 | 254 | for (char *p = tokens[i].start; p != tokens[i].stop; p++) { 255 | col[(p - str)] = tokens[i].type; 256 | } 257 | } 258 | } 259 | int main(int argc, char *argv[]) 260 | { 261 | init_GL(); 262 | 263 | if (argc == 2) { 264 | mv_ef_init(argv[1], 48.0, NULL, NULL); 265 | } 266 | 267 | char *fragment_source = mv_ef_read_entire_file("extra/vertex_shader_text.vs"); 268 | char *col = (char*)calloc(strlen(fragment_source), 1); 269 | color_string(fragment_source, col); // syntax highlighting 270 | 271 | glfwSwapInterval(1); 272 | while ( !glfwWindowShouldClose(window)) { 273 | frame_timer(); 274 | 275 | glfwPollEvents(); 276 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 277 | 278 | if (SKIP_DRAWING == 0) { 279 | float offset[2] = {0.0, 0.0}; 280 | float font_size = 18.0; 281 | 282 | float width, height; 283 | mv_ef_string_dimensions(fragment_source, &width, &height, font_size); // for potential alignment 284 | mv_ef_draw(fragment_source, col, offset, font_size); 285 | 286 | /* 287 | float font_size = 8.0; 288 | char str[MAX_STRING_LEN] = {0}; 289 | 290 | int i = 0; 291 | int ny = 106; 292 | int nx = 363; 293 | for (int j = 0; j < ny; j++) { 294 | for (int i = 0; i < nx-1; i++) { 295 | int k = j*nx + i; 296 | str[k] = 32 + 96*rng(); 297 | } 298 | str[j*nx + nx-1] = '\n'; 299 | } 300 | 301 | mv_ef_draw(str, NULL, offset, font_size); 302 | */ 303 | } 304 | 305 | glfwSwapBuffers(window); 306 | } 307 | 308 | free(fragment_source); 309 | free(col); 310 | 311 | glfwTerminate(); 312 | 313 | return 0; 314 | } 315 | 316 | void frame_timer() 317 | { 318 | static double t1 = 0.0; 319 | static double avg_dt = 0.0; 320 | static double avg_dt2 = 0.0; 321 | static int avg_counter = 0; 322 | static int num_samples = 60; 323 | 324 | double t2 = glfwGetTime(); 325 | double dt = t2-t1; 326 | t1 = t2; 327 | 328 | avg_dt += dt; 329 | avg_dt2 += dt*dt; 330 | avg_counter++; 331 | 332 | if (avg_counter == num_samples) { 333 | avg_dt /= num_samples; 334 | avg_dt2 /= num_samples; 335 | double std_dt = sqrt(avg_dt2 - avg_dt*avg_dt); 336 | double ste_dt = std_dt / sqrt(num_samples); 337 | 338 | char window_title_string[128]; 339 | sprintf(window_title_string, "dt: avg = %.3fms, std = %.3fms, ste = %.4fms. fps = %.1f", 1000.0*avg_dt, 1000.0*std_dt, 1000.0*ste_dt, 1.0/avg_dt); 340 | glfwSetWindowTitle(window, window_title_string); 341 | 342 | num_samples = 1.0/avg_dt; 343 | 344 | avg_dt = 0.0; 345 | avg_dt2 = 0.0; 346 | avg_counter = 0; 347 | } 348 | } 349 | 350 | /*****************************************************************************/ 351 | // OpenGL and GLFW boilerplate below 352 | void init_GL() 353 | { 354 | // openGL stuff 355 | printf("Initializing OpenGL/GLFW\n"); 356 | if (!glfwInit()) { 357 | printf("Could not initialize\n"); 358 | exit(-1); 359 | } 360 | glfwWindowHint(GLFW_SAMPLES, 4); // samples, for antialiasing 361 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // shader version should match these 362 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 363 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // do not use deprecated functionality 364 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 365 | 366 | window = glfwCreateWindow(resx, resy, "GLSL template", 0, 0); 367 | if (!window) { 368 | printf("Could not open glfw window\n"); 369 | glfwTerminate(); 370 | exit(-2); 371 | } 372 | glfwMakeContextCurrent(window); 373 | 374 | if(!gladLoadGL()) { 375 | printf("Something went wrong!\n"); 376 | exit(-3); 377 | } 378 | 379 | glfwSetKeyCallback(window, key_callback); 380 | glfwSetMouseButtonCallback(window, mousebutton_callback); 381 | glfwSetScrollCallback(window, mousewheel_callback); 382 | glfwSetCursorPosCallback(window, mousepos_callback); 383 | glfwSetWindowSizeCallback(window, windowsize_callback); 384 | 385 | glfwSwapInterval(0); 386 | glClearColor(39/255.0, 40/255.0, 34/255.0, 1.0); 387 | } 388 | 389 | // Callback function called every time the window size change 390 | // Adjusts the camera width and heigh so that the scale stays the same 391 | // Resets projection matrix 392 | void windowsize_callback(GLFWwindow* win, int width, int height) { 393 | 394 | (void)win; 395 | 396 | resx = width; 397 | resy = height; 398 | 399 | glViewport(0, 0, resx, resy); 400 | } 401 | 402 | // Callback function called every time a keyboard key is pressed, released or held down 403 | void key_callback(GLFWwindow* win, int key, int scancode, int action, int mods) { 404 | printf("key = %d, scancode = %d, action = %d, mods = %d\n", key, scancode, action, mods); fflush(stdout); 405 | 406 | // Close window if escape is released 407 | if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) { 408 | glfwSetWindowShouldClose(win, GL_TRUE); 409 | } 410 | 411 | if (key == GLFW_KEY_TAB && action) { 412 | SKIP_DRAWING = 1 - SKIP_DRAWING; 413 | } 414 | 415 | if (key == GLFW_KEY_SPACE && action) { 416 | int num_colors; 417 | unsigned char *colors = mv_ef_get_colors(&num_colors); 418 | 419 | for (int i = 0; i < 3*num_colors; i++) { 420 | colors[i] = 256*rng(); 421 | } 422 | mv_ef_set_colors(colors); 423 | } 424 | } 425 | 426 | // Callback function called every time a mouse button pressed or released 427 | void mousebutton_callback(GLFWwindow* win, int button, int action, int mods) { 428 | // get current cursor position, convert to world coordinates 429 | glfwGetCursorPos(win, &prevx, &prevy); 430 | double xend = prevx; 431 | double yend = prevy; 432 | 433 | printf("button = %d, action = %d, mods = %d at (%f %f)\n", button, action, mods, xend, yend); fflush(stdout); 434 | 435 | // To track the state of buttons outside this function 436 | if (action == 1) 437 | clickedButtons |= (1 << button); 438 | else 439 | clickedButtons &= ~(1 << button); 440 | 441 | 442 | // Test each button 443 | if (clickedButtons&FIRST_BUTTON) { 444 | 445 | } else if (clickedButtons&SECOND_BUTTON) { 446 | 447 | } else if (clickedButtons&THIRD_BUTTON) { 448 | 449 | } else if (clickedButtons&FOURTH_BUTTON) { 450 | 451 | } else if (clickedButtons&FIFTH_BUTTON) { 452 | 453 | } 454 | } 455 | 456 | // Callback function called every time a the mouse is moved 457 | void mousepos_callback(GLFWwindow* win, double xpos, double ypos) { 458 | (void)win; 459 | 460 | if (clickedButtons&FIRST_BUTTON) { 461 | prevx = xpos; 462 | prevy = ypos; 463 | } else if (clickedButtons&SECOND_BUTTON) { 464 | 465 | } else if (clickedButtons&THIRD_BUTTON) { 466 | 467 | } else if (clickedButtons&FOURTH_BUTTON) { 468 | 469 | } else if (clickedButtons&FIFTH_BUTTON) { 470 | 471 | } 472 | } 473 | 474 | void mousewheel_callback(GLFWwindow* win, double xoffset, double yoffset) { 475 | (void)xoffset; 476 | (void)yoffset; 477 | 478 | // double zoomFactor = pow(0.95,yoffset); 479 | 480 | glfwGetCursorPos(win, &prevx, &prevy); 481 | } 482 | 483 | 484 | //for efficent rectangle packing of bitmap font atlas 485 | //#define STB_RECT_PACK_IMPLEMENTATION 486 | //#include "stb_rect_pack.h" 487 | 488 | #define STB_TRUETYPE_IMPLEMENTATION 489 | #include "stb_truetype.h" 490 | 491 | //for generating a font.png for the bitmap font atlas 492 | //#define STB_IMAGE_WRITE_IMPLEMENTATION 493 | //#include "stb_image_write.h" 494 | 495 | #define MV_EASY_FONT_IMPLEMENTATION 496 | #include "mv_easy_font.h" --------------------------------------------------------------------------------