├── .travis.yml ├── LICENSE ├── README.md ├── redist ├── FONTLOG.txt ├── LICENSE.txt ├── PressStart2P.ttf ├── stb_truetype.h └── ttf2mono.cc ├── sample.cc ├── unifont.cpp └── unifont.hpp /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: required 3 | 4 | compiler: 5 | - clang 6 | - gcc 7 | 8 | install: 9 | - wget --quiet -O - https://raw.githubusercontent.com/r-lyeh/depot/master/travis.pre.sh | bash -x 10 | 11 | script: 12 | - wget --quiet -O - https://raw.githubusercontent.com/r-lyeh/depot/master/travis.build.sh | bash -x 13 | - wget --quiet -O - https://raw.githubusercontent.com/r-lyeh/depot/master/travis.run.sh | bash -x 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 r-lyeh (https://github.com/r-lyeh) 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | unifont 2 | ======= 3 | 4 | - Unifont is a compact and embeddable terminal 1-bpp font library (C++11). 5 | - Unifont is handy. It supports most european/greek/cyrillic unicode codepoints. 6 | - Unifont is tiny (~400 LOC), header-only, portable and cross-platform. 7 | - Unifont is aimed to gamedev and debugging sessions. 8 | - Unifont is zlib/libpng licensed. 9 | 10 | ## api 11 | - Instance an `unifont(color *framebuffer, unsigned width, color (*make_rgba)(r,g,b,a))` class. 12 | - While using an unifont class, the external framebuffer must point to a valid memory address. 13 | - Then use following methods as desired: 14 | ```c++ 15 | render_string(x,y,style,const char* utf8); 16 | render_string(x,y,style,const char* utf8, const color &c); 17 | render_string(x,y,style,const char* utf8, const color* gradient8x8); 18 | render_string(x,y,style,const vector &codepoints); 19 | render_string(x,y,style,const vector &codepoints, const color &c); 20 | render_string(x,y,style,const vector &codepoints, const color* gradient8x8); 21 | ``` 22 | - Style is an `enum { NORMAL, INVERT, SHADOW, RETRO }` flag mask. 23 | - All methods return an `struct { unsigned w, h; }` dimensions rectangle. 24 | - For a more detailed sample check [sample.cc file](sample.cc). 25 | 26 | ## preview 27 | ![image](https://raw.github.com/r-lyeh/depot/master/unifont.png) 28 | 29 | ## notes 30 | - To replace embedded built-in font see [ttf2mono.cc source code](redist/ttf2mono.cc) 31 | - Sample requires [libspot](https://github.com/r-lyeh/spot) to compile. 32 | 33 | ## licenses 34 | - [Unifont](https://github.com/r-lyeh/unifont), zlib/libpng licensed. 35 | - [PressStart2P.ttf v2.14](http://www.zone38.net/font/) by Cody "CodeMan38" Boisclair (SIL Open Font License). 36 | - [UTF-8 dfa decoder](http://bjoern.hoehrmann.de/utf-8/decoder/dfa/) by Bjoern Hoehrmann (MIT license). 37 | - Gradient retro style mask taken from [DoDonPachi arcade](http://en.wikipedia.org/wiki/DoDonPachi). 38 | 39 | ## changelog 40 | - v1.0.0 (2015/05/05) 41 | - initial revision 42 | -------------------------------------------------------------------------------- /redist/FONTLOG.txt: -------------------------------------------------------------------------------- 1 | FONTLOG for the Press Start 2P fonts 2 | 3 | This file provides detailed information on the Press Start 2P Font Software. 4 | This information should be distributed along with the Press Start 2P fonts 5 | and any derivative works. 6 | 7 | 8 | Basic Font Information 9 | 10 | Press Start 2P is a bitmap font based on the font design from 1980s Namco 11 | arcade games. Although the design of uppercase letters and digits dates 12 | back to Atari's "Sprint" (1977), the specific glyph forms in this TrueType 13 | conversion are based on those from "Return of Ishtar" (1986), one of the 14 | first games to include and regularly use lowercase as well as uppercase 15 | letters in its screen font. 16 | 17 | Unlike the original font from the "Return of Ishtar" ROM, Press Start 2P 18 | includes a wide variety of non-ASCII Unicode characters for pan-European 19 | use, including Greek and Cyrillic. 20 | 21 | 22 | ChangeLog 23 | 24 | 1 November 2012 (Cody Boisclair) Press Start 2P version 2.14 25 | - Add necessary flags for Windows to recognize as a valid Greek/Cyrillic font 26 | - Change line gap to zero to match version on Google Web Fonts 27 | 28 | 15 June 2011 (Cody Boisclair) Press Start 2P version 2.13 29 | - Correct accent marks on U+00D9 Ù and U+00DA Ú 30 | 31 | 13 June 2011 (Cody Boisclair) Press Start 2P version 2.12 32 | - Correct glyph for underscore _ 33 | - Move glyphs for Greek iota one pixel to left 34 | - Fix 'notdef' glyph, whose encoding was broken during last update 35 | 36 | 13 June 2011 (Cody Boisclair) Press Start 2P version 2.11 37 | - Retrace characters from bitmaps to fix glyph width bug 38 | - Add glyphs for Spacing Modifier Letters 39 | - Move U+201E „ one pixel to left, U+201A ‚ one pixel to right 40 | 41 | 13 June 2011 (Cody Boisclair) Press Start 2P version 2.10 42 | - Add Greek and Cyrillic alphabets. 43 | - Redesign U+0138 ĸ to match shape of Greek kappa 44 | - Correct glyph shape for grave accent ` 45 | - Move U+00B7 · down one row to appear more centered 46 | - Modify FONTLOG description to reflect Greek & Cyrillic addition 47 | 48 | 13 June 2011 (Cody Boisclair) Press Start 2P version 2.01 49 | - Correct glyph for @; improve glyphs for © and ® based on original @. 50 | 51 | 12 June 2011 (Cody Boisclair) Press Start 2P version 2.0 52 | - Initial public release. 53 | 54 | 55 | Acknowledgements 56 | 57 | If you make modifications be sure to add your name (N), email (E), 58 | web-address (if you have one) (W) and description (D). 59 | This list is in alphabetical order. 60 | 61 | N: Cody Boisclair ("CodeMan38") 62 | E: cody@zone38.net 63 | W: http://zone38.net/font/ 64 | D: Conversion of glyphs to TrueType; creation of non-ASCII glyphs 65 | 66 | N: "QTQ" 67 | E: [unknown] 68 | W: http://www22.atpages.jp/nbgifan/namco.html 69 | D: Original extraction of glyph bitmaps -------------------------------------------------------------------------------- /redist/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Cody "CodeMan38" Boisclair (cody@zone38.net), 2 | with Reserved Font Name "Press Start". 3 | 4 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 5 | This license is copied below, and is also available with a FAQ at: 6 | http://scripts.sil.org/OFL 7 | 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /redist/PressStart2P.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/r-lyeh-archived/unifont/d3053b3764bb552a061f014b0903cd82edc545c7/redist/PressStart2P.ttf -------------------------------------------------------------------------------- /redist/stb_truetype.h: -------------------------------------------------------------------------------- 1 | // stb_truetype.h - v1.05 - public domain 2 | // authored from 2009-2014 by Sean Barrett / RAD Game Tools 3 | // 4 | // This library processes TrueType files: 5 | // parse files 6 | // extract glyph metrics 7 | // extract glyph shapes 8 | // render glyphs to one-channel bitmaps with antialiasing (box filter) 9 | // 10 | // Todo: 11 | // non-MS cmaps 12 | // crashproof on bad data 13 | // hinting? (no longer patented) 14 | // cleartype-style AA? 15 | // optimize: use simple memory allocator for intermediates 16 | // optimize: build edge-list directly from curves 17 | // optimize: rasterize directly from curves? 18 | // 19 | // ADDITIONAL CONTRIBUTORS 20 | // 21 | // Mikko Mononen: compound shape support, more cmap formats 22 | // Tor Andersson: kerning, subpixel rendering 23 | // 24 | // Bug/warning reports/fixes: 25 | // "Zer" on mollyrocket (with fix) 26 | // Cass Everitt 27 | // stoiko (Haemimont Games) 28 | // Brian Hook 29 | // Walter van Niftrik 30 | // David Gow 31 | // David Given 32 | // Ivan-Assen Ivanov 33 | // Anthony Pesch 34 | // Johan Duparc 35 | // Hou Qiming 36 | // Fabian "ryg" Giesen 37 | // Martins Mozeiko 38 | // Cap Petschulat 39 | // Omar Cornut 40 | // github:aloucks 41 | // Peter LaValle 42 | // 43 | // Misc other: 44 | // Ryan Gordon 45 | // 46 | // VERSION HISTORY 47 | // 48 | // 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC 49 | // 1.04 (2015-04-15) typo in example 50 | // 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes 51 | // 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ 52 | // 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match 53 | // non-oversampled; STBTT_POINT_SIZE for packed case only 54 | // 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling 55 | // 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) 56 | // 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID 57 | // 0.8b (2014-07-07) fix a warning 58 | // 0.8 (2014-05-25) fix a few more warnings 59 | // 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back 60 | // 0.6c (2012-07-24) improve documentation 61 | // 0.6b (2012-07-20) fix a few more warnings 62 | // 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, 63 | // stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty 64 | // 0.5 (2011-12-09) bugfixes: 65 | // subpixel glyph renderer computed wrong bounding box 66 | // first vertex of shape can be off-curve (FreeSans) 67 | // 0.4b (2011-12-03) fixed an error in the font baking example 68 | // 0.4 (2011-12-01) kerning, subpixel rendering (tor) 69 | // bugfixes for: 70 | // codepoint-to-glyph conversion using table fmt=12 71 | // codepoint-to-glyph conversion using table fmt=4 72 | // stbtt_GetBakedQuad with non-square texture (Zer) 73 | // updated Hello World! sample to use kerning and subpixel 74 | // fixed some warnings 75 | // 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) 76 | // userdata, malloc-from-userdata, non-zero fill (stb) 77 | // 0.2 (2009-03-11) Fix unsigned/signed char warnings 78 | // 0.1 (2009-03-09) First public release 79 | // 80 | // LICENSE 81 | // 82 | // This software is in the public domain. Where that dedication is not 83 | // recognized, you are granted a perpetual, irrevokable license to copy 84 | // and modify this file as you see fit. 85 | // 86 | // USAGE 87 | // 88 | // Include this file in whatever places neeed to refer to it. In ONE C/C++ 89 | // file, write: 90 | // #define STB_TRUETYPE_IMPLEMENTATION 91 | // before the #include of this file. This expands out the actual 92 | // implementation into that C/C++ file. 93 | // 94 | // To make the implementation private to the file that generates the implementation, 95 | // #define STBTT_STATIC 96 | // 97 | // Simple 3D API (don't ship this, but it's fine for tools and quick start) 98 | // stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture 99 | // stbtt_GetBakedQuad() -- compute quad to draw for a given char 100 | // 101 | // Improved 3D API (more shippable): 102 | // #include "stb_rect_pack.h" -- optional, but you really want it 103 | // stbtt_PackBegin() 104 | // stbtt_PackSetOversample() -- for improved quality on small fonts 105 | // stbtt_PackFontRanges() 106 | // stbtt_PackEnd() 107 | // stbtt_GetPackedQuad() 108 | // 109 | // "Load" a font file from a memory buffer (you have to keep the buffer loaded) 110 | // stbtt_InitFont() 111 | // stbtt_GetFontOffsetForIndex() -- use for TTC font collections 112 | // 113 | // Render a unicode codepoint to a bitmap 114 | // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap 115 | // stbtt_MakeCodepointBitmap() -- renders into bitmap you provide 116 | // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be 117 | // 118 | // Character advance/positioning 119 | // stbtt_GetCodepointHMetrics() 120 | // stbtt_GetFontVMetrics() 121 | // stbtt_GetCodepointKernAdvance() 122 | // 123 | // ADDITIONAL DOCUMENTATION 124 | // 125 | // Immediately after this block comment are a series of sample programs. 126 | // 127 | // After the sample programs is the "header file" section. This section 128 | // includes documentation for each API function. 129 | // 130 | // Some important concepts to understand to use this library: 131 | // 132 | // Codepoint 133 | // Characters are defined by unicode codepoints, e.g. 65 is 134 | // uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is 135 | // the hiragana for "ma". 136 | // 137 | // Glyph 138 | // A visual character shape (every codepoint is rendered as 139 | // some glyph) 140 | // 141 | // Glyph index 142 | // A font-specific integer ID representing a glyph 143 | // 144 | // Baseline 145 | // Glyph shapes are defined relative to a baseline, which is the 146 | // bottom of uppercase characters. Characters extend both above 147 | // and below the baseline. 148 | // 149 | // Current Point 150 | // As you draw text to the screen, you keep track of a "current point" 151 | // which is the origin of each character. The current point's vertical 152 | // position is the baseline. Even "baked fonts" use this model. 153 | // 154 | // Vertical Font Metrics 155 | // The vertical qualities of the font, used to vertically position 156 | // and space the characters. See docs for stbtt_GetFontVMetrics. 157 | // 158 | // Font Size in Pixels or Points 159 | // The preferred interface for specifying font sizes in stb_truetype 160 | // is to specify how tall the font's vertical extent should be in pixels. 161 | // If that sounds good enough, skip the next paragraph. 162 | // 163 | // Most font APIs instead use "points", which are a common typographic 164 | // measurement for describing font size, defined as 72 points per inch. 165 | // stb_truetype provides a point API for compatibility. However, true 166 | // "per inch" conventions don't make much sense on computer displays 167 | // since they different monitors have different number of pixels per 168 | // inch. For example, Windows traditionally uses a convention that 169 | // there are 96 pixels per inch, thus making 'inch' measurements have 170 | // nothing to do with inches, and thus effectively defining a point to 171 | // be 1.333 pixels. Additionally, the TrueType font data provides 172 | // an explicit scale factor to scale a given font's glyphs to points, 173 | // but the author has observed that this scale factor is often wrong 174 | // for non-commercial fonts, thus making fonts scaled in points 175 | // according to the TrueType spec incoherently sized in practice. 176 | // 177 | // ADVANCED USAGE 178 | // 179 | // Quality: 180 | // 181 | // - Use the functions with Subpixel at the end to allow your characters 182 | // to have subpixel positioning. Since the font is anti-aliased, not 183 | // hinted, this is very import for quality. (This is not possible with 184 | // baked fonts.) 185 | // 186 | // - Kerning is now supported, and if you're supporting subpixel rendering 187 | // then kerning is worth using to give your text a polished look. 188 | // 189 | // Performance: 190 | // 191 | // - Convert Unicode codepoints to glyph indexes and operate on the glyphs; 192 | // if you don't do this, stb_truetype is forced to do the conversion on 193 | // every call. 194 | // 195 | // - There are a lot of memory allocations. We should modify it to take 196 | // a temp buffer and allocate from the temp buffer (without freeing), 197 | // should help performance a lot. 198 | // 199 | // NOTES 200 | // 201 | // The system uses the raw data found in the .ttf file without changing it 202 | // and without building auxiliary data structures. This is a bit inefficient 203 | // on little-endian systems (the data is big-endian), but assuming you're 204 | // caching the bitmaps or glyph shapes this shouldn't be a big deal. 205 | // 206 | // It appears to be very hard to programmatically determine what font a 207 | // given file is in a general way. I provide an API for this, but I don't 208 | // recommend it. 209 | // 210 | // 211 | // SOURCE STATISTICS (based on v0.6c, 2050 LOC) 212 | // 213 | // Documentation & header file 520 LOC \___ 660 LOC documentation 214 | // Sample code 140 LOC / 215 | // Truetype parsing 620 LOC ---- 620 LOC TrueType 216 | // Software rasterization 240 LOC \ . 217 | // Curve tesselation 120 LOC \__ 550 LOC Bitmap creation 218 | // Bitmap management 100 LOC / 219 | // Baked bitmap interface 70 LOC / 220 | // Font name matching & access 150 LOC ---- 150 221 | // C runtime library abstraction 60 LOC ---- 60 222 | 223 | 224 | ////////////////////////////////////////////////////////////////////////////// 225 | ////////////////////////////////////////////////////////////////////////////// 226 | //// 227 | //// SAMPLE PROGRAMS 228 | //// 229 | // 230 | // Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless 231 | // 232 | #if 0 233 | #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation 234 | #include "stb_truetype.h" 235 | 236 | unsigned char ttf_buffer[1<<20]; 237 | unsigned char temp_bitmap[512*512]; 238 | 239 | stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs 240 | GLuint ftex; 241 | 242 | void my_stbtt_initfont(void) 243 | { 244 | fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); 245 | stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! 246 | // can free ttf_buffer at this point 247 | glGenTextures(1, &ftex); 248 | glBindTexture(GL_TEXTURE_2D, ftex); 249 | glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); 250 | // can free temp_bitmap at this point 251 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 252 | } 253 | 254 | void my_stbtt_print(float x, float y, char *text) 255 | { 256 | // assume orthographic projection with units = screen pixels, origin at top left 257 | glEnable(GL_TEXTURE_2D); 258 | glBindTexture(GL_TEXTURE_2D, ftex); 259 | glBegin(GL_QUADS); 260 | while (*text) { 261 | if (*text >= 32 && *text < 128) { 262 | stbtt_aligned_quad q; 263 | stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 264 | glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); 265 | glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); 266 | glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); 267 | glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); 268 | } 269 | ++text; 270 | } 271 | glEnd(); 272 | } 273 | #endif 274 | // 275 | // 276 | ////////////////////////////////////////////////////////////////////////////// 277 | // 278 | // Complete program (this compiles): get a single bitmap, print as ASCII art 279 | // 280 | #if 0 281 | #include 282 | #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation 283 | #include "stb_truetype.h" 284 | 285 | char ttf_buffer[1<<25]; 286 | 287 | int main(int argc, char **argv) 288 | { 289 | stbtt_fontinfo font; 290 | unsigned char *bitmap; 291 | int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); 292 | 293 | fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); 294 | 295 | stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); 296 | bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); 297 | 298 | for (j=0; j < h; ++j) { 299 | for (i=0; i < w; ++i) 300 | putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); 301 | putchar('\n'); 302 | } 303 | return 0; 304 | } 305 | #endif 306 | // 307 | // Output: 308 | // 309 | // .ii. 310 | // @@@@@@. 311 | // V@Mio@@o 312 | // :i. V@V 313 | // :oM@@M 314 | // :@@@MM@M 315 | // @@o o@M 316 | // :@@. M@M 317 | // @@@o@@@@ 318 | // :M@@V:@@. 319 | // 320 | ////////////////////////////////////////////////////////////////////////////// 321 | // 322 | // Complete program: print "Hello World!" banner, with bugs 323 | // 324 | #if 0 325 | char buffer[24<<20]; 326 | unsigned char screen[20][79]; 327 | 328 | int main(int arg, char **argv) 329 | { 330 | stbtt_fontinfo font; 331 | int i,j,ascent,baseline,ch=0; 332 | float scale, xpos=2; // leave a little padding in case the character extends left 333 | char *text = "Heljo World!"; 334 | 335 | fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); 336 | stbtt_InitFont(&font, buffer, 0); 337 | 338 | scale = stbtt_ScaleForPixelHeight(&font, 15); 339 | stbtt_GetFontVMetrics(&font, &ascent,0,0); 340 | baseline = (int) (ascent*scale); 341 | 342 | while (text[ch]) { 343 | int advance,lsb,x0,y0,x1,y1; 344 | float x_shift = xpos - (float) floor(xpos); 345 | stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); 346 | stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); 347 | stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); 348 | // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong 349 | // because this API is really for baking character bitmaps into textures. if you want to render 350 | // a sequence of characters, you really need to render each bitmap to a temp buffer, then 351 | // "alpha blend" that into the working buffer 352 | xpos += (advance * scale); 353 | if (text[ch+1]) 354 | xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); 355 | ++ch; 356 | } 357 | 358 | for (j=0; j < 20; ++j) { 359 | for (i=0; i < 78; ++i) 360 | putchar(" .:ioVM@"[screen[j][i]>>5]); 361 | putchar('\n'); 362 | } 363 | 364 | return 0; 365 | } 366 | #endif 367 | 368 | 369 | ////////////////////////////////////////////////////////////////////////////// 370 | ////////////////////////////////////////////////////////////////////////////// 371 | //// 372 | //// INTEGRATION WITH YOUR CODEBASE 373 | //// 374 | //// The following sections allow you to supply alternate definitions 375 | //// of C library functions used by stb_truetype. 376 | 377 | #ifdef STB_TRUETYPE_IMPLEMENTATION 378 | // #define your own (u)stbtt_int8/16/32 before including to override this 379 | #ifndef stbtt_uint8 380 | typedef unsigned char stbtt_uint8; 381 | typedef signed char stbtt_int8; 382 | typedef unsigned short stbtt_uint16; 383 | typedef signed short stbtt_int16; 384 | typedef unsigned int stbtt_uint32; 385 | typedef signed int stbtt_int32; 386 | #endif 387 | 388 | typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; 389 | typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; 390 | 391 | // #define your own STBTT_sort() to override this to avoid qsort 392 | #ifndef STBTT_sort 393 | #include 394 | #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func) 395 | #endif 396 | 397 | // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h 398 | #ifndef STBTT_ifloor 399 | #include 400 | #define STBTT_ifloor(x) ((int) floor(x)) 401 | #define STBTT_iceil(x) ((int) ceil(x)) 402 | #endif 403 | 404 | #ifndef STBTT_sqrt 405 | #include 406 | #define STBTT_sqrt(x) sqrt(x) 407 | #endif 408 | 409 | // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h 410 | #ifndef STBTT_malloc 411 | #include 412 | #define STBTT_malloc(x,u) ((void)(u),malloc(x)) 413 | #define STBTT_free(x,u) ((void)(u),free(x)) 414 | #endif 415 | 416 | #ifndef STBTT_assert 417 | #include 418 | #define STBTT_assert(x) assert(x) 419 | #endif 420 | 421 | #ifndef STBTT_strlen 422 | #include 423 | #define STBTT_strlen(x) strlen(x) 424 | #endif 425 | 426 | #ifndef STBTT_memcpy 427 | #include 428 | #define STBTT_memcpy memcpy 429 | #define STBTT_memset memset 430 | #endif 431 | #endif 432 | 433 | /////////////////////////////////////////////////////////////////////////////// 434 | /////////////////////////////////////////////////////////////////////////////// 435 | //// 436 | //// INTERFACE 437 | //// 438 | //// 439 | 440 | #ifndef __STB_INCLUDE_STB_TRUETYPE_H__ 441 | #define __STB_INCLUDE_STB_TRUETYPE_H__ 442 | 443 | #ifdef STBTT_STATIC 444 | #define STBTT_DEF static 445 | #else 446 | #define STBTT_DEF extern 447 | #endif 448 | 449 | #ifdef __cplusplus 450 | extern "C" { 451 | #endif 452 | 453 | ////////////////////////////////////////////////////////////////////////////// 454 | // 455 | // TEXTURE BAKING API 456 | // 457 | // If you use this API, you only have to call two functions ever. 458 | // 459 | 460 | typedef struct 461 | { 462 | unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap 463 | float xoff,yoff,xadvance; 464 | } stbtt_bakedchar; 465 | 466 | STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) 467 | float pixel_height, // height of font in pixels 468 | unsigned char *pixels, int pw, int ph, // bitmap to be filled in 469 | int first_char, int num_chars, // characters to bake 470 | stbtt_bakedchar *chardata); // you allocate this, it's num_chars long 471 | // if return is positive, the first unused row of the bitmap 472 | // if return is negative, returns the negative of the number of characters that fit 473 | // if return is 0, no characters fit and no rows were used 474 | // This uses a very crappy packing. 475 | 476 | typedef struct 477 | { 478 | float x0,y0,s0,t0; // top-left 479 | float x1,y1,s1,t1; // bottom-right 480 | } stbtt_aligned_quad; 481 | 482 | STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above 483 | int char_index, // character to display 484 | float *xpos, float *ypos, // pointers to current position in screen pixel space 485 | stbtt_aligned_quad *q, // output: quad to draw 486 | int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier 487 | // Call GetBakedQuad with char_index = 'character - first_char', and it 488 | // creates the quad you need to draw and advances the current position. 489 | // 490 | // The coordinate system used assumes y increases downwards. 491 | // 492 | // Characters will extend both above and below the current position; 493 | // see discussion of "BASELINE" above. 494 | // 495 | // It's inefficient; you might want to c&p it and optimize it. 496 | 497 | 498 | 499 | ////////////////////////////////////////////////////////////////////////////// 500 | // 501 | // NEW TEXTURE BAKING API 502 | // 503 | // This provides options for packing multiple fonts into one atlas, not 504 | // perfectly but better than nothing. 505 | 506 | typedef struct 507 | { 508 | unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap 509 | float xoff,yoff,xadvance; 510 | float xoff2,yoff2; 511 | } stbtt_packedchar; 512 | 513 | typedef struct stbtt_pack_context stbtt_pack_context; 514 | 515 | STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); 516 | // Initializes a packing context stored in the passed-in stbtt_pack_context. 517 | // Future calls using this context will pack characters into the bitmap passed 518 | // in here: a 1-channel bitmap that is weight x height. stride_in_bytes is 519 | // the distance from one row to the next (or 0 to mean they are packed tightly 520 | // together). "padding" is // the amount of padding to leave between each 521 | // character (normally you want '1' for bitmaps you'll use as textures with 522 | // bilinear filtering). 523 | // 524 | // Returns 0 on failure, 1 on success. 525 | 526 | STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); 527 | // Cleans up the packing context and frees all memory. 528 | 529 | #define STBTT_POINT_SIZE(x) (-(x)) 530 | 531 | STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, 532 | int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); 533 | // Creates character bitmaps from the font_index'th font found in fontdata (use 534 | // font_index=0 if you don't know what that is). It creates num_chars_in_range 535 | // bitmaps for characters with unicode values starting at first_unicode_char_in_range 536 | // and increasing. Data for how to render them is stored in chardata_for_range; 537 | // pass these to stbtt_GetPackedQuad to get back renderable quads. 538 | // 539 | // font_size is the full height of the character from ascender to descender, 540 | // as computed by stbtt_ScaleForPixelHeight. To use a point size as computed 541 | // by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() 542 | // and pass that result as 'font_size': 543 | // ..., 20 , ... // font max minus min y is 20 pixels tall 544 | // ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall 545 | 546 | typedef struct 547 | { 548 | float font_size; 549 | int first_unicode_char_in_range; 550 | int num_chars_in_range; 551 | stbtt_packedchar *chardata_for_range; // output 552 | } stbtt_pack_range; 553 | 554 | STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); 555 | // Creates character bitmaps from multiple ranges of characters stored in 556 | // ranges. This will usually create a better-packed bitmap than multiple 557 | // calls to stbtt_PackFontRange. 558 | 559 | 560 | STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); 561 | // Oversampling a font increases the quality by allowing higher-quality subpixel 562 | // positioning, and is especially valuable at smaller text sizes. 563 | // 564 | // This function sets the amount of oversampling for all following calls to 565 | // stbtt_PackFontRange(s). The default (no oversampling) is achieved by 566 | // h_oversample=1, v_oversample=1. The total number of pixels required is 567 | // h_oversample*v_oversample larger than the default; for example, 2x2 568 | // oversampling requires 4x the storage of 1x1. For best results, render 569 | // oversampled textures with bilinear filtering. Look at the readme in 570 | // stb/tests/oversample for information about oversampled fonts 571 | 572 | STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above 573 | int char_index, // character to display 574 | float *xpos, float *ypos, // pointers to current position in screen pixel space 575 | stbtt_aligned_quad *q, // output: quad to draw 576 | int align_to_integer); 577 | 578 | // this is an opaque structure that you shouldn't mess with which holds 579 | // all the context needed from PackBegin to PackEnd. 580 | struct stbtt_pack_context { 581 | void *user_allocator_context; 582 | void *pack_info; 583 | int width; 584 | int height; 585 | int stride_in_bytes; 586 | int padding; 587 | unsigned int h_oversample, v_oversample; 588 | unsigned char *pixels; 589 | void *nodes; 590 | }; 591 | 592 | ////////////////////////////////////////////////////////////////////////////// 593 | // 594 | // FONT LOADING 595 | // 596 | // 597 | 598 | STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); 599 | // Each .ttf/.ttc file may have more than one font. Each font has a sequential 600 | // index number starting from 0. Call this function to get the font offset for 601 | // a given index; it returns -1 if the index is out of range. A regular .ttf 602 | // file will only define one font and it always be at offset 0, so it will 603 | // return '0' for index 0, and -1 for all other indices. You can just skip 604 | // this step if you know it's that kind of font. 605 | 606 | 607 | // The following structure is defined publically so you can declare one on 608 | // the stack or as a global or etc, but you should treat it as opaque. 609 | typedef struct stbtt_fontinfo 610 | { 611 | void * userdata; 612 | unsigned char * data; // pointer to .ttf file 613 | int fontstart; // offset of start of font 614 | 615 | int numGlyphs; // number of glyphs, needed for range checking 616 | 617 | int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf 618 | int index_map; // a cmap mapping for our chosen character encoding 619 | int indexToLocFormat; // format needed to map from glyph index to glyph 620 | } stbtt_fontinfo; 621 | 622 | STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); 623 | // Given an offset into the file that defines a font, this function builds 624 | // the necessary cached info for the rest of the system. You must allocate 625 | // the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't 626 | // need to do anything special to free it, because the contents are pure 627 | // value data with no additional data structures. Returns 0 on failure. 628 | 629 | 630 | ////////////////////////////////////////////////////////////////////////////// 631 | // 632 | // CHARACTER TO GLYPH-INDEX CONVERSIOn 633 | 634 | STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); 635 | // If you're going to perform multiple operations on the same character 636 | // and you want a speed-up, call this function with the character you're 637 | // going to process, then use glyph-based functions instead of the 638 | // codepoint-based functions. 639 | 640 | 641 | ////////////////////////////////////////////////////////////////////////////// 642 | // 643 | // CHARACTER PROPERTIES 644 | // 645 | 646 | STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); 647 | // computes a scale factor to produce a font whose "height" is 'pixels' tall. 648 | // Height is measured as the distance from the highest ascender to the lowest 649 | // descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics 650 | // and computing: 651 | // scale = pixels / (ascent - descent) 652 | // so if you prefer to measure height by the ascent only, use a similar calculation. 653 | 654 | STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); 655 | // computes a scale factor to produce a font whose EM size is mapped to 656 | // 'pixels' tall. This is probably what traditional APIs compute, but 657 | // I'm not positive. 658 | 659 | STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); 660 | // ascent is the coordinate above the baseline the font extends; descent 661 | // is the coordinate below the baseline the font extends (i.e. it is typically negative) 662 | // lineGap is the spacing between one row's descent and the next row's ascent... 663 | // so you should advance the vertical position by "*ascent - *descent + *lineGap" 664 | // these are expressed in unscaled coordinates, so you must multiply by 665 | // the scale factor for a given size 666 | 667 | STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); 668 | // the bounding box around all possible characters 669 | 670 | STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); 671 | // leftSideBearing is the offset from the current horizontal position to the left edge of the character 672 | // advanceWidth is the offset from the current horizontal position to the next horizontal position 673 | // these are expressed in unscaled coordinates 674 | 675 | STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); 676 | // an additional amount to add to the 'advance' value between ch1 and ch2 677 | 678 | STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); 679 | // Gets the bounding box of the visible part of the glyph, in unscaled coordinates 680 | 681 | STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); 682 | STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); 683 | STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); 684 | // as above, but takes one or more glyph indices for greater efficiency 685 | 686 | 687 | ////////////////////////////////////////////////////////////////////////////// 688 | // 689 | // GLYPH SHAPES (you probably don't need these, but they have to go before 690 | // the bitmaps for C declaration-order reasons) 691 | // 692 | 693 | #ifndef STBTT_vmove // you can predefine these to use different values (but why?) 694 | enum { 695 | STBTT_vmove=1, 696 | STBTT_vline, 697 | STBTT_vcurve 698 | }; 699 | #endif 700 | 701 | #ifndef stbtt_vertex // you can predefine this to use different values 702 | // (we share this with other code at RAD) 703 | #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file 704 | typedef struct 705 | { 706 | stbtt_vertex_type x,y,cx,cy; 707 | unsigned char type,padding; 708 | } stbtt_vertex; 709 | #endif 710 | 711 | STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); 712 | // returns non-zero if nothing is drawn for this glyph 713 | 714 | STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); 715 | STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); 716 | // returns # of vertices and fills *vertices with the pointer to them 717 | // these are expressed in "unscaled" coordinates 718 | // 719 | // The shape is a series of countours. Each one starts with 720 | // a STBTT_moveto, then consists of a series of mixed 721 | // STBTT_lineto and STBTT_curveto segments. A lineto 722 | // draws a line from previous endpoint to its x,y; a curveto 723 | // draws a quadratic bezier from previous endpoint to 724 | // its x,y, using cx,cy as the bezier control point. 725 | 726 | STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); 727 | // frees the data allocated above 728 | 729 | ////////////////////////////////////////////////////////////////////////////// 730 | // 731 | // BITMAP RENDERING 732 | // 733 | 734 | STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); 735 | // frees the bitmap allocated below 736 | 737 | STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); 738 | // allocates a large-enough single-channel 8bpp bitmap and renders the 739 | // specified character/glyph at the specified scale into it, with 740 | // antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). 741 | // *width & *height are filled out with the width & height of the bitmap, 742 | // which is stored left-to-right, top-to-bottom. 743 | // 744 | // xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap 745 | 746 | STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); 747 | // the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel 748 | // shift for the character 749 | 750 | STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); 751 | // the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap 752 | // in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap 753 | // is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the 754 | // width and height and positioning info for it first. 755 | 756 | STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); 757 | // same as stbtt_MakeCodepointBitmap, but you can specify a subpixel 758 | // shift for the character 759 | 760 | STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); 761 | // get the bbox of the bitmap centered around the glyph origin; so the 762 | // bitmap width is ix1-ix0, height is iy1-iy0, and location to place 763 | // the bitmap top left is (leftSideBearing*scale,iy0). 764 | // (Note that the bitmap uses y-increases-down, but the shape uses 765 | // y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) 766 | 767 | STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); 768 | // same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel 769 | // shift for the character 770 | 771 | // the following functions are equivalent to the above functions, but operate 772 | // on glyph indices instead of Unicode codepoints (for efficiency) 773 | STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); 774 | STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); 775 | STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); 776 | STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); 777 | STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); 778 | STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); 779 | 780 | 781 | // @TODO: don't expose this structure 782 | typedef struct 783 | { 784 | int w,h,stride; 785 | unsigned char *pixels; 786 | } stbtt__bitmap; 787 | 788 | STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata); 789 | 790 | ////////////////////////////////////////////////////////////////////////////// 791 | // 792 | // Finding the right font... 793 | // 794 | // You should really just solve this offline, keep your own tables 795 | // of what font is what, and don't try to get it out of the .ttf file. 796 | // That's because getting it out of the .ttf file is really hard, because 797 | // the names in the file can appear in many possible encodings, in many 798 | // possible languages, and e.g. if you need a case-insensitive comparison, 799 | // the details of that depend on the encoding & language in a complex way 800 | // (actually underspecified in truetype, but also gigantic). 801 | // 802 | // But you can use the provided functions in two possible ways: 803 | // stbtt_FindMatchingFont() will use *case-sensitive* comparisons on 804 | // unicode-encoded names to try to find the font you want; 805 | // you can run this before calling stbtt_InitFont() 806 | // 807 | // stbtt_GetFontNameString() lets you get any of the various strings 808 | // from the file yourself and do your own comparisons on them. 809 | // You have to have called stbtt_InitFont() first. 810 | 811 | 812 | STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); 813 | // returns the offset (not index) of the font that matches, or -1 if none 814 | // if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". 815 | // if you use any other flag, use a font name like "Arial"; this checks 816 | // the 'macStyle' header field; i don't know if fonts set this consistently 817 | #define STBTT_MACSTYLE_DONTCARE 0 818 | #define STBTT_MACSTYLE_BOLD 1 819 | #define STBTT_MACSTYLE_ITALIC 2 820 | #define STBTT_MACSTYLE_UNDERSCORE 4 821 | #define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 822 | 823 | STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); 824 | // returns 1/0 whether the first string interpreted as utf8 is identical to 825 | // the second string interpreted as big-endian utf16... useful for strings from next func 826 | 827 | STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); 828 | // returns the string (which may be big-endian double byte, e.g. for unicode) 829 | // and puts the length in bytes in *length. 830 | // 831 | // some of the values for the IDs are below; for more see the truetype spec: 832 | // http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html 833 | // http://www.microsoft.com/typography/otspec/name.htm 834 | 835 | enum { // platformID 836 | STBTT_PLATFORM_ID_UNICODE =0, 837 | STBTT_PLATFORM_ID_MAC =1, 838 | STBTT_PLATFORM_ID_ISO =2, 839 | STBTT_PLATFORM_ID_MICROSOFT =3 840 | }; 841 | 842 | enum { // encodingID for STBTT_PLATFORM_ID_UNICODE 843 | STBTT_UNICODE_EID_UNICODE_1_0 =0, 844 | STBTT_UNICODE_EID_UNICODE_1_1 =1, 845 | STBTT_UNICODE_EID_ISO_10646 =2, 846 | STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, 847 | STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 848 | }; 849 | 850 | enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT 851 | STBTT_MS_EID_SYMBOL =0, 852 | STBTT_MS_EID_UNICODE_BMP =1, 853 | STBTT_MS_EID_SHIFTJIS =2, 854 | STBTT_MS_EID_UNICODE_FULL =10 855 | }; 856 | 857 | enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes 858 | STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, 859 | STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, 860 | STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, 861 | STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 862 | }; 863 | 864 | enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... 865 | // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs 866 | STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, 867 | STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, 868 | STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, 869 | STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, 870 | STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, 871 | STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D 872 | }; 873 | 874 | enum { // languageID for STBTT_PLATFORM_ID_MAC 875 | STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, 876 | STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, 877 | STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, 878 | STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , 879 | STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , 880 | STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, 881 | STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 882 | }; 883 | 884 | #ifdef __cplusplus 885 | } 886 | #endif 887 | 888 | #endif // __STB_INCLUDE_STB_TRUETYPE_H__ 889 | 890 | /////////////////////////////////////////////////////////////////////////////// 891 | /////////////////////////////////////////////////////////////////////////////// 892 | //// 893 | //// IMPLEMENTATION 894 | //// 895 | //// 896 | 897 | #ifdef STB_TRUETYPE_IMPLEMENTATION 898 | 899 | #ifndef STBTT_MAX_OVERSAMPLE 900 | #define STBTT_MAX_OVERSAMPLE 8 901 | #endif 902 | 903 | typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; 904 | 905 | ////////////////////////////////////////////////////////////////////////// 906 | // 907 | // accessors to parse data from file 908 | // 909 | 910 | // on platforms that don't allow misaligned reads, if we want to allow 911 | // truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE 912 | 913 | #define ttBYTE(p) (* (stbtt_uint8 *) (p)) 914 | #define ttCHAR(p) (* (stbtt_int8 *) (p)) 915 | #define ttFixed(p) ttLONG(p) 916 | 917 | #if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) 918 | 919 | #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) 920 | #define ttSHORT(p) (* (stbtt_int16 *) (p)) 921 | #define ttULONG(p) (* (stbtt_uint32 *) (p)) 922 | #define ttLONG(p) (* (stbtt_int32 *) (p)) 923 | 924 | #else 925 | 926 | static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } 927 | static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } 928 | static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } 929 | static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } 930 | 931 | #endif 932 | 933 | #define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) 934 | #define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) 935 | 936 | static int stbtt__isfont(const stbtt_uint8 *font) 937 | { 938 | // check the version number 939 | if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 940 | if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! 941 | if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF 942 | if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 943 | return 0; 944 | } 945 | 946 | // @OPTIMIZE: binary search 947 | static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) 948 | { 949 | stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); 950 | stbtt_uint32 tabledir = fontstart + 12; 951 | stbtt_int32 i; 952 | for (i=0; i < num_tables; ++i) { 953 | stbtt_uint32 loc = tabledir + 16*i; 954 | if (stbtt_tag(data+loc+0, tag)) 955 | return ttULONG(data+loc+8); 956 | } 957 | return 0; 958 | } 959 | 960 | STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) 961 | { 962 | // if it's just a font, there's only one valid index 963 | if (stbtt__isfont(font_collection)) 964 | return index == 0 ? 0 : -1; 965 | 966 | // check if it's a TTC 967 | if (stbtt_tag(font_collection, "ttcf")) { 968 | // version 1? 969 | if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { 970 | stbtt_int32 n = ttLONG(font_collection+8); 971 | if (index >= n) 972 | return -1; 973 | return ttULONG(font_collection+12+index*14); 974 | } 975 | } 976 | return -1; 977 | } 978 | 979 | STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) 980 | { 981 | stbtt_uint8 *data = (stbtt_uint8 *) data2; 982 | stbtt_uint32 cmap, t; 983 | stbtt_int32 i,numTables; 984 | 985 | info->data = data; 986 | info->fontstart = fontstart; 987 | 988 | cmap = stbtt__find_table(data, fontstart, "cmap"); // required 989 | info->loca = stbtt__find_table(data, fontstart, "loca"); // required 990 | info->head = stbtt__find_table(data, fontstart, "head"); // required 991 | info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required 992 | info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required 993 | info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required 994 | info->kern = stbtt__find_table(data, fontstart, "kern"); // not required 995 | if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) 996 | return 0; 997 | 998 | t = stbtt__find_table(data, fontstart, "maxp"); 999 | if (t) 1000 | info->numGlyphs = ttUSHORT(data+t+4); 1001 | else 1002 | info->numGlyphs = 0xffff; 1003 | 1004 | // find a cmap encoding table we understand *now* to avoid searching 1005 | // later. (todo: could make this installable) 1006 | // the same regardless of glyph. 1007 | numTables = ttUSHORT(data + cmap + 2); 1008 | info->index_map = 0; 1009 | for (i=0; i < numTables; ++i) { 1010 | stbtt_uint32 encoding_record = cmap + 4 + 8 * i; 1011 | // find an encoding we understand: 1012 | switch(ttUSHORT(data+encoding_record)) { 1013 | case STBTT_PLATFORM_ID_MICROSOFT: 1014 | switch (ttUSHORT(data+encoding_record+2)) { 1015 | case STBTT_MS_EID_UNICODE_BMP: 1016 | case STBTT_MS_EID_UNICODE_FULL: 1017 | // MS/Unicode 1018 | info->index_map = cmap + ttULONG(data+encoding_record+4); 1019 | break; 1020 | } 1021 | break; 1022 | case STBTT_PLATFORM_ID_UNICODE: 1023 | // Mac/iOS has these 1024 | // all the encodingIDs are unicode, so we don't bother to check it 1025 | info->index_map = cmap + ttULONG(data+encoding_record+4); 1026 | break; 1027 | } 1028 | } 1029 | if (info->index_map == 0) 1030 | return 0; 1031 | 1032 | info->indexToLocFormat = ttUSHORT(data+info->head + 50); 1033 | return 1; 1034 | } 1035 | 1036 | STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) 1037 | { 1038 | stbtt_uint8 *data = info->data; 1039 | stbtt_uint32 index_map = info->index_map; 1040 | 1041 | stbtt_uint16 format = ttUSHORT(data + index_map + 0); 1042 | if (format == 0) { // apple byte encoding 1043 | stbtt_int32 bytes = ttUSHORT(data + index_map + 2); 1044 | if (unicode_codepoint < bytes-6) 1045 | return ttBYTE(data + index_map + 6 + unicode_codepoint); 1046 | return 0; 1047 | } else if (format == 6) { 1048 | stbtt_uint32 first = ttUSHORT(data + index_map + 6); 1049 | stbtt_uint32 count = ttUSHORT(data + index_map + 8); 1050 | if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) 1051 | return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); 1052 | return 0; 1053 | } else if (format == 2) { 1054 | STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean 1055 | return 0; 1056 | } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges 1057 | stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; 1058 | stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; 1059 | stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); 1060 | stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; 1061 | 1062 | // do a binary search of the segments 1063 | stbtt_uint32 endCount = index_map + 14; 1064 | stbtt_uint32 search = endCount; 1065 | 1066 | if (unicode_codepoint > 0xffff) 1067 | return 0; 1068 | 1069 | // they lie from endCount .. endCount + segCount 1070 | // but searchRange is the nearest power of two, so... 1071 | if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) 1072 | search += rangeShift*2; 1073 | 1074 | // now decrement to bias correctly to find smallest 1075 | search -= 2; 1076 | while (entrySelector) { 1077 | stbtt_uint16 end; 1078 | searchRange >>= 1; 1079 | end = ttUSHORT(data + search + searchRange*2); 1080 | if (unicode_codepoint > end) 1081 | search += searchRange*2; 1082 | --entrySelector; 1083 | } 1084 | search += 2; 1085 | 1086 | { 1087 | stbtt_uint16 offset, start; 1088 | stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); 1089 | 1090 | STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); 1091 | start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); 1092 | if (unicode_codepoint < start) 1093 | return 0; 1094 | 1095 | offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); 1096 | if (offset == 0) 1097 | return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); 1098 | 1099 | return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); 1100 | } 1101 | } else if (format == 12 || format == 13) { 1102 | stbtt_uint32 ngroups = ttULONG(data+index_map+12); 1103 | stbtt_int32 low,high; 1104 | low = 0; high = (stbtt_int32)ngroups; 1105 | // Binary search the right group. 1106 | while (low < high) { 1107 | stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high 1108 | stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); 1109 | stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); 1110 | if ((stbtt_uint32) unicode_codepoint < start_char) 1111 | high = mid; 1112 | else if ((stbtt_uint32) unicode_codepoint > end_char) 1113 | low = mid+1; 1114 | else { 1115 | stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); 1116 | if (format == 12) 1117 | return start_glyph + unicode_codepoint-start_char; 1118 | else // format == 13 1119 | return start_glyph; 1120 | } 1121 | } 1122 | return 0; // not found 1123 | } 1124 | // @TODO 1125 | STBTT_assert(0); 1126 | return 0; 1127 | } 1128 | 1129 | STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) 1130 | { 1131 | return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); 1132 | } 1133 | 1134 | static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) 1135 | { 1136 | v->type = type; 1137 | v->x = (stbtt_int16) x; 1138 | v->y = (stbtt_int16) y; 1139 | v->cx = (stbtt_int16) cx; 1140 | v->cy = (stbtt_int16) cy; 1141 | } 1142 | 1143 | static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) 1144 | { 1145 | int g1,g2; 1146 | 1147 | if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range 1148 | if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format 1149 | 1150 | if (info->indexToLocFormat == 0) { 1151 | g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; 1152 | g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; 1153 | } else { 1154 | g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); 1155 | g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); 1156 | } 1157 | 1158 | return g1==g2 ? -1 : g1; // if length is 0, return -1 1159 | } 1160 | 1161 | STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) 1162 | { 1163 | int g = stbtt__GetGlyfOffset(info, glyph_index); 1164 | if (g < 0) return 0; 1165 | 1166 | if (x0) *x0 = ttSHORT(info->data + g + 2); 1167 | if (y0) *y0 = ttSHORT(info->data + g + 4); 1168 | if (x1) *x1 = ttSHORT(info->data + g + 6); 1169 | if (y1) *y1 = ttSHORT(info->data + g + 8); 1170 | return 1; 1171 | } 1172 | 1173 | STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) 1174 | { 1175 | return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); 1176 | } 1177 | 1178 | STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) 1179 | { 1180 | stbtt_int16 numberOfContours; 1181 | int g = stbtt__GetGlyfOffset(info, glyph_index); 1182 | if (g < 0) return 1; 1183 | numberOfContours = ttSHORT(info->data + g); 1184 | return numberOfContours == 0; 1185 | } 1186 | 1187 | static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, 1188 | stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) 1189 | { 1190 | if (start_off) { 1191 | if (was_off) 1192 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); 1193 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); 1194 | } else { 1195 | if (was_off) 1196 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); 1197 | else 1198 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); 1199 | } 1200 | return num_vertices; 1201 | } 1202 | 1203 | STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) 1204 | { 1205 | stbtt_int16 numberOfContours; 1206 | stbtt_uint8 *endPtsOfContours; 1207 | stbtt_uint8 *data = info->data; 1208 | stbtt_vertex *vertices=0; 1209 | int num_vertices=0; 1210 | int g = stbtt__GetGlyfOffset(info, glyph_index); 1211 | 1212 | *pvertices = NULL; 1213 | 1214 | if (g < 0) return 0; 1215 | 1216 | numberOfContours = ttSHORT(data + g); 1217 | 1218 | if (numberOfContours > 0) { 1219 | stbtt_uint8 flags=0,flagcount; 1220 | stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; 1221 | stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; 1222 | stbtt_uint8 *points; 1223 | endPtsOfContours = (data + g + 10); 1224 | ins = ttUSHORT(data + g + 10 + numberOfContours * 2); 1225 | points = data + g + 10 + numberOfContours * 2 + 2 + ins; 1226 | 1227 | n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); 1228 | 1229 | m = n + 2*numberOfContours; // a loose bound on how many vertices we might need 1230 | vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); 1231 | if (vertices == 0) 1232 | return 0; 1233 | 1234 | next_move = 0; 1235 | flagcount=0; 1236 | 1237 | // in first pass, we load uninterpreted data into the allocated array 1238 | // above, shifted to the end of the array so we won't overwrite it when 1239 | // we create our final data starting from the front 1240 | 1241 | off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated 1242 | 1243 | // first load flags 1244 | 1245 | for (i=0; i < n; ++i) { 1246 | if (flagcount == 0) { 1247 | flags = *points++; 1248 | if (flags & 8) 1249 | flagcount = *points++; 1250 | } else 1251 | --flagcount; 1252 | vertices[off+i].type = flags; 1253 | } 1254 | 1255 | // now load x coordinates 1256 | x=0; 1257 | for (i=0; i < n; ++i) { 1258 | flags = vertices[off+i].type; 1259 | if (flags & 2) { 1260 | stbtt_int16 dx = *points++; 1261 | x += (flags & 16) ? dx : -dx; // ??? 1262 | } else { 1263 | if (!(flags & 16)) { 1264 | x = x + (stbtt_int16) (points[0]*256 + points[1]); 1265 | points += 2; 1266 | } 1267 | } 1268 | vertices[off+i].x = (stbtt_int16) x; 1269 | } 1270 | 1271 | // now load y coordinates 1272 | y=0; 1273 | for (i=0; i < n; ++i) { 1274 | flags = vertices[off+i].type; 1275 | if (flags & 4) { 1276 | stbtt_int16 dy = *points++; 1277 | y += (flags & 32) ? dy : -dy; // ??? 1278 | } else { 1279 | if (!(flags & 32)) { 1280 | y = y + (stbtt_int16) (points[0]*256 + points[1]); 1281 | points += 2; 1282 | } 1283 | } 1284 | vertices[off+i].y = (stbtt_int16) y; 1285 | } 1286 | 1287 | // now convert them to our format 1288 | num_vertices=0; 1289 | sx = sy = cx = cy = scx = scy = 0; 1290 | for (i=0; i < n; ++i) { 1291 | flags = vertices[off+i].type; 1292 | x = (stbtt_int16) vertices[off+i].x; 1293 | y = (stbtt_int16) vertices[off+i].y; 1294 | 1295 | if (next_move == i) { 1296 | if (i != 0) 1297 | num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 1298 | 1299 | // now start the new one 1300 | start_off = !(flags & 1); 1301 | if (start_off) { 1302 | // if we start off with an off-curve point, then when we need to find a point on the curve 1303 | // where we can start, and we need to save some state for when we wraparound. 1304 | scx = x; 1305 | scy = y; 1306 | if (!(vertices[off+i+1].type & 1)) { 1307 | // next point is also a curve point, so interpolate an on-point curve 1308 | sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; 1309 | sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; 1310 | } else { 1311 | // otherwise just use the next point as our start point 1312 | sx = (stbtt_int32) vertices[off+i+1].x; 1313 | sy = (stbtt_int32) vertices[off+i+1].y; 1314 | ++i; // we're using point i+1 as the starting point, so skip it 1315 | } 1316 | } else { 1317 | sx = x; 1318 | sy = y; 1319 | } 1320 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); 1321 | was_off = 0; 1322 | next_move = 1 + ttUSHORT(endPtsOfContours+j*2); 1323 | ++j; 1324 | } else { 1325 | if (!(flags & 1)) { // if it's a curve 1326 | if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint 1327 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); 1328 | cx = x; 1329 | cy = y; 1330 | was_off = 1; 1331 | } else { 1332 | if (was_off) 1333 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); 1334 | else 1335 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); 1336 | was_off = 0; 1337 | } 1338 | } 1339 | } 1340 | num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 1341 | } else if (numberOfContours == -1) { 1342 | // Compound shapes. 1343 | int more = 1; 1344 | stbtt_uint8 *comp = data + g + 10; 1345 | num_vertices = 0; 1346 | vertices = 0; 1347 | while (more) { 1348 | stbtt_uint16 flags, gidx; 1349 | int comp_num_verts = 0, i; 1350 | stbtt_vertex *comp_verts = 0, *tmp = 0; 1351 | float mtx[6] = {1,0,0,1,0,0}, m, n; 1352 | 1353 | flags = ttSHORT(comp); comp+=2; 1354 | gidx = ttSHORT(comp); comp+=2; 1355 | 1356 | if (flags & 2) { // XY values 1357 | if (flags & 1) { // shorts 1358 | mtx[4] = ttSHORT(comp); comp+=2; 1359 | mtx[5] = ttSHORT(comp); comp+=2; 1360 | } else { 1361 | mtx[4] = ttCHAR(comp); comp+=1; 1362 | mtx[5] = ttCHAR(comp); comp+=1; 1363 | } 1364 | } 1365 | else { 1366 | // @TODO handle matching point 1367 | STBTT_assert(0); 1368 | } 1369 | if (flags & (1<<3)) { // WE_HAVE_A_SCALE 1370 | mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 1371 | mtx[1] = mtx[2] = 0; 1372 | } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE 1373 | mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; 1374 | mtx[1] = mtx[2] = 0; 1375 | mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 1376 | } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO 1377 | mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; 1378 | mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; 1379 | mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; 1380 | mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 1381 | } 1382 | 1383 | // Find transformation scales. 1384 | m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); 1385 | n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); 1386 | 1387 | // Get indexed glyph. 1388 | comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); 1389 | if (comp_num_verts > 0) { 1390 | // Transform vertices. 1391 | for (i = 0; i < comp_num_verts; ++i) { 1392 | stbtt_vertex* v = &comp_verts[i]; 1393 | stbtt_vertex_type x,y; 1394 | x=v->x; y=v->y; 1395 | v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); 1396 | v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); 1397 | x=v->cx; y=v->cy; 1398 | v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); 1399 | v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); 1400 | } 1401 | // Append vertices. 1402 | tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); 1403 | if (!tmp) { 1404 | if (vertices) STBTT_free(vertices, info->userdata); 1405 | if (comp_verts) STBTT_free(comp_verts, info->userdata); 1406 | return 0; 1407 | } 1408 | if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); 1409 | STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); 1410 | if (vertices) STBTT_free(vertices, info->userdata); 1411 | vertices = tmp; 1412 | STBTT_free(comp_verts, info->userdata); 1413 | num_vertices += comp_num_verts; 1414 | } 1415 | // More components ? 1416 | more = flags & (1<<5); 1417 | } 1418 | } else if (numberOfContours < 0) { 1419 | // @TODO other compound variations? 1420 | STBTT_assert(0); 1421 | } else { 1422 | // numberOfCounters == 0, do nothing 1423 | } 1424 | 1425 | *pvertices = vertices; 1426 | return num_vertices; 1427 | } 1428 | 1429 | STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) 1430 | { 1431 | stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); 1432 | if (glyph_index < numOfLongHorMetrics) { 1433 | if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); 1434 | if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); 1435 | } else { 1436 | if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); 1437 | if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); 1438 | } 1439 | } 1440 | 1441 | STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) 1442 | { 1443 | stbtt_uint8 *data = info->data + info->kern; 1444 | stbtt_uint32 needle, straw; 1445 | int l, r, m; 1446 | 1447 | // we only look at the first table. it must be 'horizontal' and format 0. 1448 | if (!info->kern) 1449 | return 0; 1450 | if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 1451 | return 0; 1452 | if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format 1453 | return 0; 1454 | 1455 | l = 0; 1456 | r = ttUSHORT(data+10) - 1; 1457 | needle = glyph1 << 16 | glyph2; 1458 | while (l <= r) { 1459 | m = (l + r) >> 1; 1460 | straw = ttULONG(data+18+(m*6)); // note: unaligned read 1461 | if (needle < straw) 1462 | r = m - 1; 1463 | else if (needle > straw) 1464 | l = m + 1; 1465 | else 1466 | return ttSHORT(data+22+(m*6)); 1467 | } 1468 | return 0; 1469 | } 1470 | 1471 | STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) 1472 | { 1473 | if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs 1474 | return 0; 1475 | return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); 1476 | } 1477 | 1478 | STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) 1479 | { 1480 | stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); 1481 | } 1482 | 1483 | STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) 1484 | { 1485 | if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); 1486 | if (descent) *descent = ttSHORT(info->data+info->hhea + 6); 1487 | if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); 1488 | } 1489 | 1490 | STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) 1491 | { 1492 | *x0 = ttSHORT(info->data + info->head + 36); 1493 | *y0 = ttSHORT(info->data + info->head + 38); 1494 | *x1 = ttSHORT(info->data + info->head + 40); 1495 | *y1 = ttSHORT(info->data + info->head + 42); 1496 | } 1497 | 1498 | STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) 1499 | { 1500 | int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); 1501 | return (float) height / fheight; 1502 | } 1503 | 1504 | STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) 1505 | { 1506 | int unitsPerEm = ttUSHORT(info->data + info->head + 18); 1507 | return pixels / unitsPerEm; 1508 | } 1509 | 1510 | STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) 1511 | { 1512 | STBTT_free(v, info->userdata); 1513 | } 1514 | 1515 | ////////////////////////////////////////////////////////////////////////////// 1516 | // 1517 | // antialiasing software rasterizer 1518 | // 1519 | 1520 | STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) 1521 | { 1522 | int x0,y0,x1,y1; 1523 | if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { 1524 | // e.g. space character 1525 | if (ix0) *ix0 = 0; 1526 | if (iy0) *iy0 = 0; 1527 | if (ix1) *ix1 = 0; 1528 | if (iy1) *iy1 = 0; 1529 | } else { 1530 | // move to integral bboxes (treating pixels as little squares, what pixels get touched)? 1531 | if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); 1532 | if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); 1533 | if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); 1534 | if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); 1535 | } 1536 | } 1537 | 1538 | STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) 1539 | { 1540 | stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); 1541 | } 1542 | 1543 | STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) 1544 | { 1545 | stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); 1546 | } 1547 | 1548 | STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) 1549 | { 1550 | stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); 1551 | } 1552 | 1553 | typedef struct stbtt__edge { 1554 | float x0,y0, x1,y1; 1555 | int invert; 1556 | } stbtt__edge; 1557 | 1558 | typedef struct stbtt__active_edge 1559 | { 1560 | int x,dx; 1561 | float ey; 1562 | struct stbtt__active_edge *next; 1563 | int valid; 1564 | } stbtt__active_edge; 1565 | 1566 | #define FIXSHIFT 10 1567 | #define FIX (1 << FIXSHIFT) 1568 | #define FIXMASK (FIX-1) 1569 | 1570 | static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata) 1571 | { 1572 | stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!! 1573 | float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); 1574 | STBTT_assert(e->y0 <= start_point); 1575 | if (!z) return z; 1576 | // round dx down to avoid going too far 1577 | if (dxdy < 0) 1578 | z->dx = -STBTT_ifloor(FIX * -dxdy); 1579 | else 1580 | z->dx = STBTT_ifloor(FIX * dxdy); 1581 | z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0))); 1582 | z->x -= off_x * FIX; 1583 | z->ey = e->y1; 1584 | z->next = 0; 1585 | z->valid = e->invert ? 1 : -1; 1586 | return z; 1587 | } 1588 | 1589 | // note: this routine clips fills that extend off the edges... ideally this 1590 | // wouldn't happen, but it could happen if the truetype glyph bounding boxes 1591 | // are wrong, or if the user supplies a too-small bitmap 1592 | static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) 1593 | { 1594 | // non-zero winding fill 1595 | int x0=0, w=0; 1596 | 1597 | while (e) { 1598 | if (w == 0) { 1599 | // if we're currently at zero, we need to record the edge start point 1600 | x0 = e->x; w += e->valid; 1601 | } else { 1602 | int x1 = e->x; w += e->valid; 1603 | // if we went to zero, we need to draw 1604 | if (w == 0) { 1605 | int i = x0 >> FIXSHIFT; 1606 | int j = x1 >> FIXSHIFT; 1607 | 1608 | if (i < len && j >= 0) { 1609 | if (i == j) { 1610 | // x0,x1 are the same pixel, so compute combined coverage 1611 | scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT); 1612 | } else { 1613 | if (i >= 0) // add antialiasing for x0 1614 | scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT); 1615 | else 1616 | i = -1; // clip 1617 | 1618 | if (j < len) // add antialiasing for x1 1619 | scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT); 1620 | else 1621 | j = len; // clip 1622 | 1623 | for (++i; i < j; ++i) // fill pixels between x0 and x1 1624 | scanline[i] = scanline[i] + (stbtt_uint8) max_weight; 1625 | } 1626 | } 1627 | } 1628 | } 1629 | 1630 | e = e->next; 1631 | } 1632 | } 1633 | 1634 | static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) 1635 | { 1636 | stbtt__active_edge *active = NULL; 1637 | int y,j=0; 1638 | int max_weight = (255 / vsubsample); // weight per vertical scanline 1639 | int s; // vertical subsample index 1640 | unsigned char scanline_data[512], *scanline; 1641 | 1642 | if (result->w > 512) 1643 | scanline = (unsigned char *) STBTT_malloc(result->w, userdata); 1644 | else 1645 | scanline = scanline_data; 1646 | 1647 | y = off_y * vsubsample; 1648 | e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; 1649 | 1650 | while (j < result->h) { 1651 | STBTT_memset(scanline, 0, result->w); 1652 | for (s=0; s < vsubsample; ++s) { 1653 | // find center of pixel for this scanline 1654 | float scan_y = y + 0.5f; 1655 | stbtt__active_edge **step = &active; 1656 | 1657 | // update all active edges; 1658 | // remove all active edges that terminate before the center of this scanline 1659 | while (*step) { 1660 | stbtt__active_edge * z = *step; 1661 | if (z->ey <= scan_y) { 1662 | *step = z->next; // delete from list 1663 | STBTT_assert(z->valid); 1664 | z->valid = 0; 1665 | STBTT_free(z, userdata); 1666 | } else { 1667 | z->x += z->dx; // advance to position for current scanline 1668 | step = &((*step)->next); // advance through list 1669 | } 1670 | } 1671 | 1672 | // resort the list if needed 1673 | for(;;) { 1674 | int changed=0; 1675 | step = &active; 1676 | while (*step && (*step)->next) { 1677 | if ((*step)->x > (*step)->next->x) { 1678 | stbtt__active_edge *t = *step; 1679 | stbtt__active_edge *q = t->next; 1680 | 1681 | t->next = q->next; 1682 | q->next = t; 1683 | *step = q; 1684 | changed = 1; 1685 | } 1686 | step = &(*step)->next; 1687 | } 1688 | if (!changed) break; 1689 | } 1690 | 1691 | // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline 1692 | while (e->y0 <= scan_y) { 1693 | if (e->y1 > scan_y) { 1694 | stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata); 1695 | // find insertion point 1696 | if (active == NULL) 1697 | active = z; 1698 | else if (z->x < active->x) { 1699 | // insert at front 1700 | z->next = active; 1701 | active = z; 1702 | } else { 1703 | // find thing to insert AFTER 1704 | stbtt__active_edge *p = active; 1705 | while (p->next && p->next->x < z->x) 1706 | p = p->next; 1707 | // at this point, p->next->x is NOT < z->x 1708 | z->next = p->next; 1709 | p->next = z; 1710 | } 1711 | } 1712 | ++e; 1713 | } 1714 | 1715 | // now process all active edges in XOR fashion 1716 | if (active) 1717 | stbtt__fill_active_edges(scanline, result->w, active, max_weight); 1718 | 1719 | ++y; 1720 | } 1721 | STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); 1722 | ++j; 1723 | } 1724 | 1725 | while (active) { 1726 | stbtt__active_edge *z = active; 1727 | active = active->next; 1728 | STBTT_free(z, userdata); 1729 | } 1730 | 1731 | if (scanline != scanline_data) 1732 | STBTT_free(scanline, userdata); 1733 | } 1734 | 1735 | static int stbtt__edge_compare(const void *p, const void *q) 1736 | { 1737 | stbtt__edge *a = (stbtt__edge *) p; 1738 | stbtt__edge *b = (stbtt__edge *) q; 1739 | 1740 | if (a->y0 < b->y0) return -1; 1741 | if (a->y0 > b->y0) return 1; 1742 | return 0; 1743 | } 1744 | 1745 | typedef struct 1746 | { 1747 | float x,y; 1748 | } stbtt__point; 1749 | 1750 | static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) 1751 | { 1752 | float y_scale_inv = invert ? -scale_y : scale_y; 1753 | stbtt__edge *e; 1754 | int n,i,j,k,m; 1755 | int vsubsample = result->h < 8 ? 15 : 5; 1756 | // vsubsample should divide 255 evenly; otherwise we won't reach full opacity 1757 | 1758 | // now we have to blow out the windings into explicit edge lists 1759 | n = 0; 1760 | for (i=0; i < windings; ++i) 1761 | n += wcount[i]; 1762 | 1763 | e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel 1764 | if (e == 0) return; 1765 | n = 0; 1766 | 1767 | m=0; 1768 | for (i=0; i < windings; ++i) { 1769 | stbtt__point *p = pts + m; 1770 | m += wcount[i]; 1771 | j = wcount[i]-1; 1772 | for (k=0; k < wcount[i]; j=k++) { 1773 | int a=k,b=j; 1774 | // skip the edge if horizontal 1775 | if (p[j].y == p[k].y) 1776 | continue; 1777 | // add edge from j to k to the list 1778 | e[n].invert = 0; 1779 | if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { 1780 | e[n].invert = 1; 1781 | a=j,b=k; 1782 | } 1783 | e[n].x0 = p[a].x * scale_x + shift_x; 1784 | e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; 1785 | e[n].x1 = p[b].x * scale_x + shift_x; 1786 | e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; 1787 | ++n; 1788 | } 1789 | } 1790 | 1791 | // now sort the edges by their highest point (should snap to integer, and then by x) 1792 | STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); 1793 | 1794 | // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule 1795 | stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); 1796 | 1797 | STBTT_free(e, userdata); 1798 | } 1799 | 1800 | static void stbtt__add_point(stbtt__point *points, int n, float x, float y) 1801 | { 1802 | if (!points) return; // during first pass, it's unallocated 1803 | points[n].x = x; 1804 | points[n].y = y; 1805 | } 1806 | 1807 | // tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching 1808 | static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) 1809 | { 1810 | // midpoint 1811 | float mx = (x0 + 2*x1 + x2)/4; 1812 | float my = (y0 + 2*y1 + y2)/4; 1813 | // versus directly drawn line 1814 | float dx = (x0+x2)/2 - mx; 1815 | float dy = (y0+y2)/2 - my; 1816 | if (n > 16) // 65536 segments on one curve better be enough! 1817 | return 1; 1818 | if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA 1819 | stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); 1820 | stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); 1821 | } else { 1822 | stbtt__add_point(points, *num_points,x2,y2); 1823 | *num_points = *num_points+1; 1824 | } 1825 | return 1; 1826 | } 1827 | 1828 | // returns number of contours 1829 | static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) 1830 | { 1831 | stbtt__point *points=0; 1832 | int num_points=0; 1833 | 1834 | float objspace_flatness_squared = objspace_flatness * objspace_flatness; 1835 | int i,n=0,start=0, pass; 1836 | 1837 | // count how many "moves" there are to get the contour count 1838 | for (i=0; i < num_verts; ++i) 1839 | if (vertices[i].type == STBTT_vmove) 1840 | ++n; 1841 | 1842 | *num_contours = n; 1843 | if (n == 0) return 0; 1844 | 1845 | *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); 1846 | 1847 | if (*contour_lengths == 0) { 1848 | *num_contours = 0; 1849 | return 0; 1850 | } 1851 | 1852 | // make two passes through the points so we don't need to realloc 1853 | for (pass=0; pass < 2; ++pass) { 1854 | float x=0,y=0; 1855 | if (pass == 1) { 1856 | points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); 1857 | if (points == NULL) goto error; 1858 | } 1859 | num_points = 0; 1860 | n= -1; 1861 | for (i=0; i < num_verts; ++i) { 1862 | switch (vertices[i].type) { 1863 | case STBTT_vmove: 1864 | // start the next contour 1865 | if (n >= 0) 1866 | (*contour_lengths)[n] = num_points - start; 1867 | ++n; 1868 | start = num_points; 1869 | 1870 | x = vertices[i].x, y = vertices[i].y; 1871 | stbtt__add_point(points, num_points++, x,y); 1872 | break; 1873 | case STBTT_vline: 1874 | x = vertices[i].x, y = vertices[i].y; 1875 | stbtt__add_point(points, num_points++, x, y); 1876 | break; 1877 | case STBTT_vcurve: 1878 | stbtt__tesselate_curve(points, &num_points, x,y, 1879 | vertices[i].cx, vertices[i].cy, 1880 | vertices[i].x, vertices[i].y, 1881 | objspace_flatness_squared, 0); 1882 | x = vertices[i].x, y = vertices[i].y; 1883 | break; 1884 | } 1885 | } 1886 | (*contour_lengths)[n] = num_points - start; 1887 | } 1888 | 1889 | return points; 1890 | error: 1891 | STBTT_free(points, userdata); 1892 | STBTT_free(*contour_lengths, userdata); 1893 | *contour_lengths = 0; 1894 | *num_contours = 0; 1895 | return NULL; 1896 | } 1897 | 1898 | STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) 1899 | { 1900 | float scale = scale_x > scale_y ? scale_y : scale_x; 1901 | int winding_count, *winding_lengths; 1902 | stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); 1903 | if (windings) { 1904 | stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); 1905 | STBTT_free(winding_lengths, userdata); 1906 | STBTT_free(windings, userdata); 1907 | } 1908 | } 1909 | 1910 | STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) 1911 | { 1912 | STBTT_free(bitmap, userdata); 1913 | } 1914 | 1915 | STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) 1916 | { 1917 | int ix0,iy0,ix1,iy1; 1918 | stbtt__bitmap gbm; 1919 | stbtt_vertex *vertices; 1920 | int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 1921 | 1922 | if (scale_x == 0) scale_x = scale_y; 1923 | if (scale_y == 0) { 1924 | if (scale_x == 0) return NULL; 1925 | scale_y = scale_x; 1926 | } 1927 | 1928 | stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); 1929 | 1930 | // now we get the size 1931 | gbm.w = (ix1 - ix0); 1932 | gbm.h = (iy1 - iy0); 1933 | gbm.pixels = NULL; // in case we error 1934 | 1935 | if (width ) *width = gbm.w; 1936 | if (height) *height = gbm.h; 1937 | if (xoff ) *xoff = ix0; 1938 | if (yoff ) *yoff = iy0; 1939 | 1940 | if (gbm.w && gbm.h) { 1941 | gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); 1942 | if (gbm.pixels) { 1943 | gbm.stride = gbm.w; 1944 | 1945 | stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); 1946 | } 1947 | } 1948 | STBTT_free(vertices, info->userdata); 1949 | return gbm.pixels; 1950 | } 1951 | 1952 | STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) 1953 | { 1954 | return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); 1955 | } 1956 | 1957 | STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) 1958 | { 1959 | int ix0,iy0; 1960 | stbtt_vertex *vertices; 1961 | int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 1962 | stbtt__bitmap gbm; 1963 | 1964 | stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); 1965 | gbm.pixels = output; 1966 | gbm.w = out_w; 1967 | gbm.h = out_h; 1968 | gbm.stride = out_stride; 1969 | 1970 | if (gbm.w && gbm.h) 1971 | stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); 1972 | 1973 | STBTT_free(vertices, info->userdata); 1974 | } 1975 | 1976 | STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) 1977 | { 1978 | stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); 1979 | } 1980 | 1981 | STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) 1982 | { 1983 | return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); 1984 | } 1985 | 1986 | STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) 1987 | { 1988 | stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); 1989 | } 1990 | 1991 | STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) 1992 | { 1993 | return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); 1994 | } 1995 | 1996 | STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) 1997 | { 1998 | stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); 1999 | } 2000 | 2001 | ////////////////////////////////////////////////////////////////////////////// 2002 | // 2003 | // bitmap baking 2004 | // 2005 | // This is SUPER-CRAPPY packing to keep source code small 2006 | 2007 | STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) 2008 | float pixel_height, // height of font in pixels 2009 | unsigned char *pixels, int pw, int ph, // bitmap to be filled in 2010 | int first_char, int num_chars, // characters to bake 2011 | stbtt_bakedchar *chardata) 2012 | { 2013 | float scale; 2014 | int x,y,bottom_y, i; 2015 | stbtt_fontinfo f; 2016 | if (!stbtt_InitFont(&f, data, offset)) 2017 | return -1; 2018 | STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels 2019 | x=y=1; 2020 | bottom_y = 1; 2021 | 2022 | scale = stbtt_ScaleForPixelHeight(&f, pixel_height); 2023 | 2024 | for (i=0; i < num_chars; ++i) { 2025 | int advance, lsb, x0,y0,x1,y1,gw,gh; 2026 | int g = stbtt_FindGlyphIndex(&f, first_char + i); 2027 | stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); 2028 | stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); 2029 | gw = x1-x0; 2030 | gh = y1-y0; 2031 | if (x + gw + 1 >= pw) 2032 | y = bottom_y, x = 1; // advance to next row 2033 | if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row 2034 | return -i; 2035 | STBTT_assert(x+gw < pw); 2036 | STBTT_assert(y+gh < ph); 2037 | stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); 2038 | chardata[i].x0 = (stbtt_int16) x; 2039 | chardata[i].y0 = (stbtt_int16) y; 2040 | chardata[i].x1 = (stbtt_int16) (x + gw); 2041 | chardata[i].y1 = (stbtt_int16) (y + gh); 2042 | chardata[i].xadvance = scale * advance; 2043 | chardata[i].xoff = (float) x0; 2044 | chardata[i].yoff = (float) y0; 2045 | x = x + gw + 1; 2046 | if (y+gh+1 > bottom_y) 2047 | bottom_y = y+gh+1; 2048 | } 2049 | return bottom_y; 2050 | } 2051 | 2052 | STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) 2053 | { 2054 | float d3d_bias = opengl_fillrule ? 0 : -0.5f; 2055 | float ipw = 1.0f / pw, iph = 1.0f / ph; 2056 | stbtt_bakedchar *b = chardata + char_index; 2057 | int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); 2058 | int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); 2059 | 2060 | q->x0 = round_x + d3d_bias; 2061 | q->y0 = round_y + d3d_bias; 2062 | q->x1 = round_x + b->x1 - b->x0 + d3d_bias; 2063 | q->y1 = round_y + b->y1 - b->y0 + d3d_bias; 2064 | 2065 | q->s0 = b->x0 * ipw; 2066 | q->t0 = b->y0 * iph; 2067 | q->s1 = b->x1 * ipw; 2068 | q->t1 = b->y1 * iph; 2069 | 2070 | *xpos += b->xadvance; 2071 | } 2072 | 2073 | ////////////////////////////////////////////////////////////////////////////// 2074 | // 2075 | // rectangle packing replacement routines if you don't have stb_rect_pack.h 2076 | // 2077 | 2078 | #ifndef STB_RECT_PACK_VERSION 2079 | #ifdef _MSC_VER 2080 | #define STBTT__NOTUSED(v) (void)(v) 2081 | #else 2082 | #define STBTT__NOTUSED(v) (void)sizeof(v) 2083 | #endif 2084 | 2085 | typedef int stbrp_coord; 2086 | 2087 | //////////////////////////////////////////////////////////////////////////////////// 2088 | // // 2089 | // // 2090 | // COMPILER WARNING ?!?!? // 2091 | // // 2092 | // // 2093 | // if you get a compile warning due to these symbols being defined more than // 2094 | // once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // 2095 | // // 2096 | //////////////////////////////////////////////////////////////////////////////////// 2097 | 2098 | typedef struct 2099 | { 2100 | int width,height; 2101 | int x,y,bottom_y; 2102 | } stbrp_context; 2103 | 2104 | typedef struct 2105 | { 2106 | unsigned char x; 2107 | } stbrp_node; 2108 | 2109 | typedef struct 2110 | { 2111 | stbrp_coord x,y; 2112 | int id,w,h,was_packed; 2113 | } stbrp_rect; 2114 | 2115 | static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) 2116 | { 2117 | con->width = pw; 2118 | con->height = ph; 2119 | con->x = 0; 2120 | con->y = 0; 2121 | con->bottom_y = 0; 2122 | STBTT__NOTUSED(nodes); 2123 | STBTT__NOTUSED(num_nodes); 2124 | } 2125 | 2126 | static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) 2127 | { 2128 | int i; 2129 | for (i=0; i < num_rects; ++i) { 2130 | if (con->x + rects[i].w > con->width) { 2131 | con->x = 0; 2132 | con->y = con->bottom_y; 2133 | } 2134 | if (con->y + rects[i].h > con->height) 2135 | break; 2136 | rects[i].x = con->x; 2137 | rects[i].y = con->y; 2138 | rects[i].was_packed = 1; 2139 | con->x += rects[i].w; 2140 | if (con->y + rects[i].h > con->bottom_y) 2141 | con->bottom_y = con->y + rects[i].h; 2142 | } 2143 | for ( ; i < num_rects; ++i) 2144 | rects[i].was_packed = 0; 2145 | } 2146 | #endif 2147 | 2148 | ////////////////////////////////////////////////////////////////////////////// 2149 | // 2150 | // bitmap baking 2151 | // 2152 | // This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If 2153 | // stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. 2154 | 2155 | STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) 2156 | { 2157 | stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); 2158 | int num_nodes = pw - padding; 2159 | stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); 2160 | 2161 | if (context == NULL || nodes == NULL) { 2162 | if (context != NULL) STBTT_free(context, alloc_context); 2163 | if (nodes != NULL) STBTT_free(nodes , alloc_context); 2164 | return 0; 2165 | } 2166 | 2167 | spc->user_allocator_context = alloc_context; 2168 | spc->width = pw; 2169 | spc->height = ph; 2170 | spc->pixels = pixels; 2171 | spc->pack_info = context; 2172 | spc->nodes = nodes; 2173 | spc->padding = padding; 2174 | spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; 2175 | spc->h_oversample = 1; 2176 | spc->v_oversample = 1; 2177 | 2178 | stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); 2179 | 2180 | STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels 2181 | 2182 | return 1; 2183 | } 2184 | 2185 | STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) 2186 | { 2187 | STBTT_free(spc->nodes , spc->user_allocator_context); 2188 | STBTT_free(spc->pack_info, spc->user_allocator_context); 2189 | } 2190 | 2191 | STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) 2192 | { 2193 | STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); 2194 | STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); 2195 | if (h_oversample <= STBTT_MAX_OVERSAMPLE) 2196 | spc->h_oversample = h_oversample; 2197 | if (v_oversample <= STBTT_MAX_OVERSAMPLE) 2198 | spc->v_oversample = v_oversample; 2199 | } 2200 | 2201 | #define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) 2202 | 2203 | static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) 2204 | { 2205 | unsigned char buffer[STBTT_MAX_OVERSAMPLE]; 2206 | int safe_w = w - kernel_width; 2207 | int j; 2208 | for (j=0; j < h; ++j) { 2209 | int i; 2210 | unsigned int total; 2211 | STBTT_memset(buffer, 0, kernel_width); 2212 | 2213 | total = 0; 2214 | 2215 | // make kernel_width a constant in common cases so compiler can optimize out the divide 2216 | switch (kernel_width) { 2217 | case 2: 2218 | for (i=0; i <= safe_w; ++i) { 2219 | total += pixels[i] - buffer[i & STBTT__OVER_MASK]; 2220 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; 2221 | pixels[i] = (unsigned char) (total / 2); 2222 | } 2223 | break; 2224 | case 3: 2225 | for (i=0; i <= safe_w; ++i) { 2226 | total += pixels[i] - buffer[i & STBTT__OVER_MASK]; 2227 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; 2228 | pixels[i] = (unsigned char) (total / 3); 2229 | } 2230 | break; 2231 | case 4: 2232 | for (i=0; i <= safe_w; ++i) { 2233 | total += pixels[i] - buffer[i & STBTT__OVER_MASK]; 2234 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; 2235 | pixels[i] = (unsigned char) (total / 4); 2236 | } 2237 | break; 2238 | default: 2239 | for (i=0; i <= safe_w; ++i) { 2240 | total += pixels[i] - buffer[i & STBTT__OVER_MASK]; 2241 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; 2242 | pixels[i] = (unsigned char) (total / kernel_width); 2243 | } 2244 | break; 2245 | } 2246 | 2247 | for (; i < w; ++i) { 2248 | STBTT_assert(pixels[i] == 0); 2249 | total -= buffer[i & STBTT__OVER_MASK]; 2250 | pixels[i] = (unsigned char) (total / kernel_width); 2251 | } 2252 | 2253 | pixels += stride_in_bytes; 2254 | } 2255 | } 2256 | 2257 | static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) 2258 | { 2259 | unsigned char buffer[STBTT_MAX_OVERSAMPLE]; 2260 | int safe_h = h - kernel_width; 2261 | int j; 2262 | for (j=0; j < w; ++j) { 2263 | int i; 2264 | unsigned int total; 2265 | STBTT_memset(buffer, 0, kernel_width); 2266 | 2267 | total = 0; 2268 | 2269 | // make kernel_width a constant in common cases so compiler can optimize out the divide 2270 | switch (kernel_width) { 2271 | case 2: 2272 | for (i=0; i <= safe_h; ++i) { 2273 | total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; 2274 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; 2275 | pixels[i*stride_in_bytes] = (unsigned char) (total / 2); 2276 | } 2277 | break; 2278 | case 3: 2279 | for (i=0; i <= safe_h; ++i) { 2280 | total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; 2281 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; 2282 | pixels[i*stride_in_bytes] = (unsigned char) (total / 3); 2283 | } 2284 | break; 2285 | case 4: 2286 | for (i=0; i <= safe_h; ++i) { 2287 | total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; 2288 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; 2289 | pixels[i*stride_in_bytes] = (unsigned char) (total / 4); 2290 | } 2291 | break; 2292 | default: 2293 | for (i=0; i <= safe_h; ++i) { 2294 | total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; 2295 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; 2296 | pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); 2297 | } 2298 | break; 2299 | } 2300 | 2301 | for (; i < h; ++i) { 2302 | STBTT_assert(pixels[i*stride_in_bytes] == 0); 2303 | total -= buffer[i & STBTT__OVER_MASK]; 2304 | pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); 2305 | } 2306 | 2307 | pixels += 1; 2308 | } 2309 | } 2310 | 2311 | static float stbtt__oversample_shift(int oversample) 2312 | { 2313 | if (!oversample) 2314 | return 0.0f; 2315 | 2316 | // The prefilter is a box filter of width "oversample", 2317 | // which shifts phase by (oversample - 1)/2 pixels in 2318 | // oversampled space. We want to shift in the opposite 2319 | // direction to counter this. 2320 | return (float)-(oversample - 1) / (2.0f * (float)oversample); 2321 | } 2322 | 2323 | STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) 2324 | { 2325 | stbtt_fontinfo info; 2326 | float recip_h = 1.0f / spc->h_oversample; 2327 | float recip_v = 1.0f / spc->v_oversample; 2328 | float sub_x = stbtt__oversample_shift(spc->h_oversample); 2329 | float sub_y = stbtt__oversample_shift(spc->v_oversample); 2330 | int i,j,k,n, return_value = 1; 2331 | stbrp_context *context = (stbrp_context *) spc->pack_info; 2332 | stbrp_rect *rects; 2333 | 2334 | // flag all characters as NOT packed 2335 | for (i=0; i < num_ranges; ++i) 2336 | for (j=0; j < ranges[i].num_chars_in_range; ++j) 2337 | ranges[i].chardata_for_range[j].x0 = 2338 | ranges[i].chardata_for_range[j].y0 = 2339 | ranges[i].chardata_for_range[j].x1 = 2340 | ranges[i].chardata_for_range[j].y1 = 0; 2341 | 2342 | n = 0; 2343 | for (i=0; i < num_ranges; ++i) 2344 | n += ranges[i].num_chars_in_range; 2345 | 2346 | rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); 2347 | if (rects == NULL) 2348 | return 0; 2349 | 2350 | stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); 2351 | k=0; 2352 | for (i=0; i < num_ranges; ++i) { 2353 | float fh = ranges[i].font_size; 2354 | float scale = fh > 0 ? stbtt_ScaleForPixelHeight(&info, fh) : stbtt_ScaleForMappingEmToPixels(&info, -fh); 2355 | for (j=0; j < ranges[i].num_chars_in_range; ++j) { 2356 | int x0,y0,x1,y1; 2357 | stbtt_GetCodepointBitmapBoxSubpixel(&info, ranges[i].first_unicode_char_in_range + j, 2358 | scale * spc->h_oversample, 2359 | scale * spc->v_oversample, 2360 | 0,0, 2361 | &x0,&y0,&x1,&y1); 2362 | rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); 2363 | rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); 2364 | ++k; 2365 | } 2366 | } 2367 | 2368 | stbrp_pack_rects(context, rects, k); 2369 | 2370 | k = 0; 2371 | for (i=0; i < num_ranges; ++i) { 2372 | float fh = ranges[i].font_size; 2373 | float scale = fh > 0 ? stbtt_ScaleForPixelHeight(&info, fh) : stbtt_ScaleForMappingEmToPixels(&info, -fh); 2374 | for (j=0; j < ranges[i].num_chars_in_range; ++j) { 2375 | stbrp_rect *r = &rects[k]; 2376 | if (r->was_packed) { 2377 | stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; 2378 | int advance, lsb, x0,y0,x1,y1; 2379 | int glyph = stbtt_FindGlyphIndex(&info, ranges[i].first_unicode_char_in_range + j); 2380 | stbrp_coord pad = (stbrp_coord) spc->padding; 2381 | 2382 | // pad on left and top 2383 | r->x += pad; 2384 | r->y += pad; 2385 | r->w -= pad; 2386 | r->h -= pad; 2387 | stbtt_GetGlyphHMetrics(&info, glyph, &advance, &lsb); 2388 | stbtt_GetGlyphBitmapBox(&info, glyph, 2389 | scale * spc->h_oversample, 2390 | scale * spc->v_oversample, 2391 | &x0,&y0,&x1,&y1); 2392 | stbtt_MakeGlyphBitmapSubpixel(&info, 2393 | spc->pixels + r->x + r->y*spc->stride_in_bytes, 2394 | r->w - spc->h_oversample+1, 2395 | r->h - spc->v_oversample+1, 2396 | spc->stride_in_bytes, 2397 | scale * spc->h_oversample, 2398 | scale * spc->v_oversample, 2399 | 0,0, 2400 | glyph); 2401 | 2402 | if (spc->h_oversample > 1) 2403 | stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, 2404 | r->w, r->h, spc->stride_in_bytes, 2405 | spc->h_oversample); 2406 | 2407 | if (spc->v_oversample > 1) 2408 | stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, 2409 | r->w, r->h, spc->stride_in_bytes, 2410 | spc->v_oversample); 2411 | 2412 | bc->x0 = (stbtt_int16) r->x; 2413 | bc->y0 = (stbtt_int16) r->y; 2414 | bc->x1 = (stbtt_int16) (r->x + r->w); 2415 | bc->y1 = (stbtt_int16) (r->y + r->h); 2416 | bc->xadvance = scale * advance; 2417 | bc->xoff = (float) x0 * recip_h + sub_x; 2418 | bc->yoff = (float) y0 * recip_v + sub_y; 2419 | bc->xoff2 = (x0 + r->w) * recip_h + sub_x; 2420 | bc->yoff2 = (y0 + r->h) * recip_v + sub_y; 2421 | } else { 2422 | return_value = 0; // if any fail, report failure 2423 | } 2424 | 2425 | ++k; 2426 | } 2427 | } 2428 | 2429 | STBTT_free(rects, spc->user_allocator_context); 2430 | return return_value; 2431 | } 2432 | 2433 | STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, 2434 | int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) 2435 | { 2436 | stbtt_pack_range range; 2437 | range.first_unicode_char_in_range = first_unicode_char_in_range; 2438 | range.num_chars_in_range = num_chars_in_range; 2439 | range.chardata_for_range = chardata_for_range; 2440 | range.font_size = font_size; 2441 | return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); 2442 | } 2443 | 2444 | STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) 2445 | { 2446 | float ipw = 1.0f / pw, iph = 1.0f / ph; 2447 | stbtt_packedchar *b = chardata + char_index; 2448 | 2449 | if (align_to_integer) { 2450 | float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); 2451 | float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); 2452 | q->x0 = x; 2453 | q->y0 = y; 2454 | q->x1 = x + b->xoff2 - b->xoff; 2455 | q->y1 = y + b->yoff2 - b->yoff; 2456 | } else { 2457 | q->x0 = *xpos + b->xoff; 2458 | q->y0 = *ypos + b->yoff; 2459 | q->x1 = *xpos + b->xoff2; 2460 | q->y1 = *ypos + b->yoff2; 2461 | } 2462 | 2463 | q->s0 = b->x0 * ipw; 2464 | q->t0 = b->y0 * iph; 2465 | q->s1 = b->x1 * ipw; 2466 | q->t1 = b->y1 * iph; 2467 | 2468 | *xpos += b->xadvance; 2469 | } 2470 | 2471 | 2472 | ////////////////////////////////////////////////////////////////////////////// 2473 | // 2474 | // font name matching -- recommended not to use this 2475 | // 2476 | 2477 | // check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string 2478 | static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) 2479 | { 2480 | stbtt_int32 i=0; 2481 | 2482 | // convert utf16 to utf8 and compare the results while converting 2483 | while (len2) { 2484 | stbtt_uint16 ch = s2[0]*256 + s2[1]; 2485 | if (ch < 0x80) { 2486 | if (i >= len1) return -1; 2487 | if (s1[i++] != ch) return -1; 2488 | } else if (ch < 0x800) { 2489 | if (i+1 >= len1) return -1; 2490 | if (s1[i++] != 0xc0 + (ch >> 6)) return -1; 2491 | if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; 2492 | } else if (ch >= 0xd800 && ch < 0xdc00) { 2493 | stbtt_uint32 c; 2494 | stbtt_uint16 ch2 = s2[2]*256 + s2[3]; 2495 | if (i+3 >= len1) return -1; 2496 | c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; 2497 | if (s1[i++] != 0xf0 + (c >> 18)) return -1; 2498 | if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; 2499 | if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; 2500 | if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; 2501 | s2 += 2; // plus another 2 below 2502 | len2 -= 2; 2503 | } else if (ch >= 0xdc00 && ch < 0xe000) { 2504 | return -1; 2505 | } else { 2506 | if (i+2 >= len1) return -1; 2507 | if (s1[i++] != 0xe0 + (ch >> 12)) return -1; 2508 | if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; 2509 | if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; 2510 | } 2511 | s2 += 2; 2512 | len2 -= 2; 2513 | } 2514 | return i; 2515 | } 2516 | 2517 | STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) 2518 | { 2519 | return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); 2520 | } 2521 | 2522 | // returns results in whatever encoding you request... but note that 2-byte encodings 2523 | // will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare 2524 | STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) 2525 | { 2526 | stbtt_int32 i,count,stringOffset; 2527 | stbtt_uint8 *fc = font->data; 2528 | stbtt_uint32 offset = font->fontstart; 2529 | stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); 2530 | if (!nm) return NULL; 2531 | 2532 | count = ttUSHORT(fc+nm+2); 2533 | stringOffset = nm + ttUSHORT(fc+nm+4); 2534 | for (i=0; i < count; ++i) { 2535 | stbtt_uint32 loc = nm + 6 + 12 * i; 2536 | if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) 2537 | && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { 2538 | *length = ttUSHORT(fc+loc+8); 2539 | return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); 2540 | } 2541 | } 2542 | return NULL; 2543 | } 2544 | 2545 | static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) 2546 | { 2547 | stbtt_int32 i; 2548 | stbtt_int32 count = ttUSHORT(fc+nm+2); 2549 | stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); 2550 | 2551 | for (i=0; i < count; ++i) { 2552 | stbtt_uint32 loc = nm + 6 + 12 * i; 2553 | stbtt_int32 id = ttUSHORT(fc+loc+6); 2554 | if (id == target_id) { 2555 | // find the encoding 2556 | stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); 2557 | 2558 | // is this a Unicode encoding? 2559 | if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { 2560 | stbtt_int32 slen = ttUSHORT(fc+loc+8); 2561 | stbtt_int32 off = ttUSHORT(fc+loc+10); 2562 | 2563 | // check if there's a prefix match 2564 | stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); 2565 | if (matchlen >= 0) { 2566 | // check for target_id+1 immediately following, with same encoding & language 2567 | if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { 2568 | slen = ttUSHORT(fc+loc+12+8); 2569 | off = ttUSHORT(fc+loc+12+10); 2570 | if (slen == 0) { 2571 | if (matchlen == nlen) 2572 | return 1; 2573 | } else if (matchlen < nlen && name[matchlen] == ' ') { 2574 | ++matchlen; 2575 | if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) 2576 | return 1; 2577 | } 2578 | } else { 2579 | // if nothing immediately following 2580 | if (matchlen == nlen) 2581 | return 1; 2582 | } 2583 | } 2584 | } 2585 | 2586 | // @TODO handle other encodings 2587 | } 2588 | } 2589 | return 0; 2590 | } 2591 | 2592 | static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) 2593 | { 2594 | stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); 2595 | stbtt_uint32 nm,hd; 2596 | if (!stbtt__isfont(fc+offset)) return 0; 2597 | 2598 | // check italics/bold/underline flags in macStyle... 2599 | if (flags) { 2600 | hd = stbtt__find_table(fc, offset, "head"); 2601 | if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; 2602 | } 2603 | 2604 | nm = stbtt__find_table(fc, offset, "name"); 2605 | if (!nm) return 0; 2606 | 2607 | if (flags) { 2608 | // if we checked the macStyle flags, then just check the family and ignore the subfamily 2609 | if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; 2610 | if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; 2611 | if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; 2612 | } else { 2613 | if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; 2614 | if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; 2615 | if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; 2616 | } 2617 | 2618 | return 0; 2619 | } 2620 | 2621 | STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) 2622 | { 2623 | stbtt_int32 i; 2624 | for (i=0;;++i) { 2625 | stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); 2626 | if (off < 0) return off; 2627 | if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) 2628 | return off; 2629 | } 2630 | } 2631 | 2632 | #endif // STB_TRUETYPE_IMPLEMENTATION 2633 | -------------------------------------------------------------------------------- /redist/ttf2mono.cc: -------------------------------------------------------------------------------- 1 | // ttf2mono font converter, dirty hack :) 2 | // r-lyeh, public domain 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation 14 | #include "stb_truetype.h" 15 | 16 | char ttf_buffer[1<<25]; 17 | stbtt_fontinfo font; 18 | 19 | int main(int argc, char **argv) 20 | { 21 | if( argc < 2 ) { 22 | std::cout << "usage: " << argv[0] << " font.ttf" << std::endl; 23 | return -1; 24 | } 25 | 26 | std::string filename = argv[1]; 27 | std::string title = filename; 28 | title = title.substr( title.find_last_of('/') + 1 ); 29 | title = title.substr( title.find_last_of('\\') + 1 ); 30 | title = title.substr( 0, title.find_last_of('.') ); 31 | 32 | FILE *fp = fopen(filename.c_str(), "rb"); 33 | fread(ttf_buffer, 1, 1<<25, fp); 34 | fclose(fp); 35 | 36 | stbtt_InitFont(&font, (const unsigned char *)ttf_buffer, stbtt_GetFontOffsetForIndex((const unsigned char *)ttf_buffer,0)); 37 | 38 | auto mk_monospace = [&]( std::vector &bitfont, std::vector &toc, int c, int MAX ) { 39 | int w, h, xoff, yoff, s = 8; 40 | unsigned char *bitmap = 0; 41 | 42 | // this is used to determinate if given 'c' is valid or invalid codepoint/unsupported typeface 43 | static const unsigned char *invalid = 0; 44 | static int size = 0; 45 | if( !invalid ) { 46 | int invalid_codepoint = 0xC0C1; 47 | bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, &xoff, &yoff); 48 | invalid = (unsigned char *)malloc( size = w * h ); 49 | memcpy( (unsigned char *)invalid, bitmap, size ); 50 | for( unsigned it = 0; it < size; ++it ) { 51 | printf( &"\"\\x%02x%s"[ !!(it % w) ], invalid[it], &"\"},\n\0\"}};\n"[ (1+it) < size ? 4 * !!((1+it) % w) : 5 ] ); 52 | } 53 | } 54 | 55 | bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, &xoff, &yoff); 56 | 57 | if( w*h == size && !memcmp(invalid, bitmap, size) ) { 58 | return 0; 59 | } 60 | 61 | toc.push_back( c ); 62 | 63 | std::vector font; 64 | printf("u+%04x %d %d %d %d\n", c, w, h, xoff, yoff); 65 | for(int j = 0; j < MAX; ++j) { 66 | font.push_back( 0 ); 67 | if( j >= MAX + yoff && j < (MAX + yoff) + h ) { 68 | for( int i=0; i < MAX; ++i ) { 69 | char ch = i >= xoff && i < xoff + w ? ".x"[(bitmap[(j-(MAX+yoff))*w+i-xoff]>>5) > 0] : '.'; 70 | font.back() |= (ch != '.') << (MAX - 1 - i); 71 | putchar( ch ); 72 | } 73 | } else { 74 | printf("%s", std::string(MAX, '.').c_str() ); 75 | } 76 | printf(" 0x%02x [%d]\n", font.back(), c*MAX+j ); 77 | } 78 | for( auto &it : font ) { 79 | bitfont.push_back( it ); 80 | } 81 | return 1; 82 | }; 83 | 84 | puts("/* generated by ttf2mono.cc; https://github.com/r-lyeh */"); 85 | puts("/*"); 86 | 87 | std::vector monofont, toc; 88 | for( uint32_t x = 0; x <= 0xffff; ++x ) { 89 | mk_monospace(monofont, toc, x, 8); 90 | } 91 | 92 | puts("*/"); 93 | 94 | printf("%s", "#ifndef __cplusplus\n"); 95 | 96 | printf("const unsigned char %s_ttf[] =\n", title.c_str() ); 97 | for( auto end = monofont.size(), it = end - end; it < end; ++it ) { 98 | printf( &"\"\\x%02x%s"[ !!(it % 16) ], monofont[it], &"\"\n\0\";\n"[ (1+it) < end ? 2 * !!((1+it) % 16) : 3 ] ); 99 | } 100 | 101 | printf("const int %s_ttf_toc[] = {\n", title.c_str() ); 102 | for( auto end = toc.size(), it = end - end; it < end; ++it ) { 103 | printf( "%6d%s", toc[it], &",\0,\n\0};\n"[ (1+it) >= end ? 5 : 2 * !((1+it) % 16) ] ); 104 | } 105 | 106 | printf("%s", "" 107 | "/*dichotomic binary search (container must be sorted && supporting sequential access); r-lyeh, public domain */\n" 108 | "static inline unsigned toc_find( const int *begin, const int *end, int x ) {\n" 109 | " unsigned min = 0, max = unsigned( (end - begin) / sizeof(int));\n" 110 | " while( min < max ) {\n" 111 | " unsigned mid = min + ( max - min ) / 2;\n" 112 | " /**/ if( x == begin[mid] ) return mid;\n" 113 | " else if( x < begin[mid] ) max = mid;\n" 114 | " else min = mid + 1;\n" 115 | " }\n" 116 | " return ~0u;\n" 117 | "};\n"); 118 | 119 | printf("%s", "#else /* __cplusplus */\n"); 120 | 121 | /* 122 | printf("const std::map %s_map_cpp8 = {\n", title.c_str() ); 123 | for( auto end = monofont.size(), it = end - end; it < end; ++it ) { 124 | if( 0 == (it%8) ) { 125 | printf("{0x%04x,", toc[it/8] ); 126 | } 127 | printf( &"\"\\x%02x%s"[ !!(it % 8) ], monofont[it], &"\"},\n\0\"}};\n"[ (1+it) < end ? 4 * !!((1+it) % 8) : 5 ] ); 128 | } 129 | */ 130 | 131 | char buf[256]; 132 | printf("const std::map %s_ttf = {\n", title.c_str() ); 133 | for( auto end = monofont.size(), it = end - end; it < end; ++it ) { 134 | char *ptr = buf; 135 | if( 0 == (it%8) ) { 136 | ptr += sprintf(ptr, "{0x%04x,", toc[it/8] ); 137 | } 138 | ptr += sprintf(ptr, &"0x%02x"[ 2 * !!(it % 8) ], monofont[it] ); 139 | if( (1+it) >= end ) { 140 | ptr += sprintf(ptr, "%s", "}};\n" ); 141 | } else { 142 | if( !((1+it) % 8) ) ptr += sprintf(ptr, "%s", "},"); 143 | if( !((1+it) %24) ) ptr += sprintf(ptr, "%s", "\n"); 144 | } 145 | printf( "%s", buf ); 146 | } 147 | 148 | printf("%s", "#endif /* __cplusplus */\n"); 149 | 150 | return 0; 151 | } 152 | -------------------------------------------------------------------------------- /sample.cc: -------------------------------------------------------------------------------- 1 | // sample requires libspot (https://github.com/r-lyeh/spot) 2 | // file must be saved with utf-8 encoding 3 | // r-lyeh, public domain 4 | 5 | #include "unifont.hpp" 6 | 7 | #if !defined(USE_LIBSPOT) 8 | 9 | int main() { 10 | 11 | } 12 | 13 | #else 14 | 15 | #include "spot/spot.hpp" 16 | #include "spot/samples/cimg.hpp" 17 | 18 | spot::pixel make_rgba( uint8_t r, uint8_t g, uint8_t b, uint8_t a ) { 19 | return spot::rgba(r,g,b,a); 20 | } 21 | 22 | int main() { 23 | spot::texture img(600, 600); 24 | for( unsigned y = 0; y < 600; ++y ) { 25 | for( unsigned x = 0; x < 600; ++x ) { 26 | img.at( x, y ) = spot::hsla( ( (x+y) % 255 ) / 255.0, (x+y) / 600.0, (x+y) / 1200.0, 1 ); 27 | } 28 | } 29 | 30 | unifont font( &img[0], img.w, &make_rgba ); 31 | 32 | // styles 33 | int normal = font.NORMAL; 34 | int invert = font.INVERT; 35 | int shadow = font.SHADOW; 36 | int retro = font.RETRO; 37 | 38 | // coord 39 | int ox = 10, oy = 10; 40 | 41 | oy += font.render_string( ox, oy, 0|2, "ee: õueaiaäär" ).h; 42 | oy += font.render_string( ox, oy, 0|2, "es: ¡¿Cómo que por qué se escribe cigüeña?!" ).h; 43 | oy += font.render_string( ox, oy, 0|2, "fi: Vesihiisi sihisi hississä." ).h; 44 | oy += font.render_string( ox, oy, 0|2, "fr: D'où êtes-vous ? Ça va? Allô? Pouvez-vous répéter, s'il vous plaît?" ).h; 45 | oy += font.render_string( ox, oy, 0|2, "ge: ბაყაყი წყალში ყიყინებს (unsupported)" ).h; 46 | oy += font.render_string( ox, oy, 0|2, "gr: Χάρηκα για την γνωρημία. Η αθασιά της Αϊσές αν έχει αθάσια, ας έχει" ).h; 47 | oy += font.render_string( ox, oy, 0|2, "hr: Cvrči cvrči cvrčak na čvoru crne smrče" ).h; 48 | oy += font.render_string( ox, oy, 0|2, "is: Ég get etið gler án þess að meiða mig" ).h; 49 | oy += font.render_string( ox, oy, 0|2, "pl: Słucham. Dzięki. Skąd jesteś?" ).h; 50 | oy += font.render_string( ox, oy, 0|2, "ro: Mulţumesc! Însănătoșire grabnică! Îți doresc." ).h; 51 | oy += font.render_string( ox, oy, 0|2, "ru: Будем здоровы!" ).h; 52 | oy += font.render_string( ox, oy, 0|2, "**: (unsupported) أَلَمٌ أَلَمَّ أَلَمْ أُلِمَّ بِدَائِهِ ... إِنْ آنَ آنٌ آنَ آنُ أَوَانِهِ" ).h; 53 | 54 | oy += font.render_string( ox, oy, 0, "+-------------------------------------+" ).h; 55 | oy += font.render_string( ox, oy, 0, "| HELLO WORLD!! $01234567.89# abc ABC |X" ).h; 56 | oy += font.render_string( ox, oy, 0, "+-------------------------------------+X" ).h; 57 | oy += font.render_string( ox, oy, 0, " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ).h; 58 | 59 | oy += font.render_string( ox, oy, 0, "yellow: color test without shadow", make_rgba(255,255,0,255) ).h; 60 | oy += font.render_string( ox, oy, 0|shadow, "cherry: color test with shadow", make_rgba(222,49,99,255) ).h; 61 | 62 | oy += font.render_string( ox, oy, normal, "style: plain" ).h; 63 | oy += font.render_string( ox, oy, invert, "style: invert" ).h; 64 | oy += font.render_string( ox, oy, shadow, "style: shadow" ).h; 65 | oy += font.render_string( ox, oy, retro, "style: retro" ).h; 66 | oy += font.render_string( ox, oy, retro|shadow, "style: retro+shadow" ).h; 67 | auto A = make_rgba(255,255,255,255), B = make_rgba(0,255,255,255), C = make_rgba(0,128,255,255), D = make_rgba(40,80,255,255); 68 | decltype(A) gradient[] = { 69 | A,A,A,A,A,A,A,A, 70 | A,A,A,A,A,A,A,A, 71 | B,B,B,B,B,B,B,B, 72 | B,B,B,B,B,B,B,B, 73 | C,C,C,C,C,C,C,C, 74 | C,C,C,C,C,C,C,C, 75 | D,D,D,D,D,D,D,D, 76 | D,D,D,D,D,D,D,D, 77 | }; 78 | oy += font.render_string( ox, oy, shadow, "style: custom gradient fill", gradient ).h; 79 | 80 | oy += font.render_glyphs( ox, oy, shadow ).h; 81 | 82 | img.copy( 0, 0, 380, 520 ).save_as_png("unifont.png"); 83 | display( img, "mono fonts preview" ); 84 | } 85 | 86 | #endif 87 | 88 | -------------------------------------------------------------------------------- /unifont.cpp: -------------------------------------------------------------------------------- 1 | #include "unifont.hpp" 2 | -------------------------------------------------------------------------------- /unifont.hpp: -------------------------------------------------------------------------------- 1 | // an embeddable/compact console font that supports most european/greek/cyrillic unicode codepoints. aimed to gamedev. 2 | // - rlyeh, zlib/libpng licensed. 3 | 4 | // using: 5 | // - [PressStart2P.ttf v2.14](http://www.zone38.net/font/) by Cody "CodeMan38" Boisclair (SIL Open Font License). 6 | // - [UTF-8 dfa decoder](http://bjoern.hoehrmann.de/utf-8/decoder/dfa/) by Bjoern Hoehrmann (MIT license). 7 | // - Gradient retro style mask taken from [DoDonPachi arcade](http://en.wikipedia.org/wiki/DoDonPachi). 8 | 9 | #pragma once 10 | #include 11 | #include 12 | #include 13 | 14 | #define UNIFONT_VERSION "1.0.0" // " (2015/05/05) - initial revision" 15 | 16 | template 17 | struct unifont { 18 | 19 | struct dim { 20 | uint32_t w, h; 21 | }; 22 | 23 | enum { 24 | NORMAL = 0, 25 | INVERT = 1, 26 | SHADOW = 2, 27 | RETRO = 4, 28 | }; 29 | 30 | struct framebuffer { 31 | color *pixel; 32 | uint32_t width; 33 | color (*make_rgba)( uint8_t r, uint8_t g, uint8_t b, uint8_t a ); 34 | } fb; 35 | 36 | std::vector gradient; // precomputed gradient colors [8][8] 37 | std::vector flat; // precomputed flat colors [8][8] 38 | color Z, W, P; // precomputed base colors 39 | 40 | unifont( color *pixel, uint32_t width, color (*make_rgba)( uint8_t r, uint8_t g, uint8_t b, uint8_t a ) ) 41 | : fb( framebuffer { pixel, width, make_rgba } ) 42 | { 43 | Z = (*fb.make_rgba)( 0, 0, 0, 255 ); 44 | W = (*fb.make_rgba)( 255, 255, 255, 255 ); 45 | P = (*fb.make_rgba)( 255, 0, 255, 255 ); 46 | 47 | color A = (*fb.make_rgba)( 255, 165, 82, 255 ); 48 | color B = (*fb.make_rgba)( 255, 115, 33, 255 ); 49 | color C = (*fb.make_rgba)( 247, 66, 16, 255 ); 50 | color D = (*fb.make_rgba)( 206, 24, 8, 255 ); 51 | color E = (*fb.make_rgba)( 156, 0, 0, 255 ); 52 | color F = (*fb.make_rgba)( 115, 0, 0, 255 ); 53 | 54 | flat = decltype(flat)( 64, W ); // default white (flat) gradient 55 | gradient = decltype(gradient) { // do don pachi gradient style :) 56 | A,A,A,A,A,B,B,C, 57 | A,A,A,A,B,B,C,C, 58 | A,A,A,B,B,C,C,D, 59 | A,A,B,B,C,C,D,D, 60 | A,B,B,C,C,D,D,E, 61 | B,B,C,C,D,D,E,E, 62 | B,C,C,D,D,E,E,F, 63 | C,C,D,D,E,E,F,F 64 | }; 65 | } 66 | 67 | dim render_line( int x0, int y0, int x1, int y1, const color &c ) const { 68 | if( x1 == x0 ) { 69 | for( ; y0 < y1 ; ++y0 ) { 70 | fb.pixel[ x0 + y0 * fb.width ] = c; 71 | } 72 | } else { 73 | float deltax = x1 - x0; 74 | float deltay = y1 - y0; 75 | float error = 0.0f; 76 | float deltaerr = abs(deltay / deltax); 77 | float y = y0; 78 | for( float x = x0; x < x1; ++x) { 79 | fb.pixel[ (uint32_t)(x + y * fb.width) ] = c; 80 | error += deltaerr; 81 | while( error >= 0.5f ) { 82 | fb.pixel[ (uint32_t)(x + y * fb.width) ] = c; 83 | y += y1 - y0 > 0 ? 1 : -1; 84 | error -= 1.0f; 85 | } 86 | } 87 | } 88 | return dim { unsigned(x1 - x0), unsigned(y1 - y0) }; 89 | } 90 | 91 | dim render_string( int ox, int oy, int mode, const std::vector &glyphs, const color *mask8x8 = 0 ) const { 92 | if( !mask8x8 ) mask8x8 = (mode & 4 ? &gradient[0] : &flat[0]); 93 | 94 | auto *ptr = &fb.pixel[ ox + oy * fb.width ]; 95 | for( const auto &rune : glyphs ) { 96 | for( unsigned y = 0; y < 8; ++y ) { 97 | const unsigned y64 = ( 7 - y ) * 8; 98 | if( mode & SHADOW ) { 99 | ptr += fb.width + 1; 100 | ptr[0] = rune & (0x80ULL << y64) ? Z : ptr[0]; 101 | ptr[1] = rune & (0x40ULL << y64) ? Z : ptr[1]; 102 | ptr[2] = rune & (0x20ULL << y64) ? Z : ptr[2]; 103 | ptr[3] = rune & (0x10ULL << y64) ? Z : ptr[3]; 104 | ptr[4] = rune & (0x08ULL << y64) ? Z : ptr[4]; 105 | ptr[5] = rune & (0x04ULL << y64) ? Z : ptr[5]; 106 | ptr[6] = rune & (0x02ULL << y64) ? Z : ptr[6]; 107 | ptr[7] = rune & (0x01ULL << y64) ? Z : ptr[7]; 108 | ptr -= fb.width + 1; 109 | } 110 | if( mode & INVERT ) { 111 | ptr[0] = rune & (0x80ULL << y64) ? ptr[0] : mask8x8[0+y*8]; 112 | ptr[1] = rune & (0x40ULL << y64) ? ptr[1] : mask8x8[1+y*8]; 113 | ptr[2] = rune & (0x20ULL << y64) ? ptr[2] : mask8x8[2+y*8]; 114 | ptr[3] = rune & (0x10ULL << y64) ? ptr[3] : mask8x8[3+y*8]; 115 | ptr[4] = rune & (0x08ULL << y64) ? ptr[4] : mask8x8[4+y*8]; 116 | ptr[5] = rune & (0x04ULL << y64) ? ptr[5] : mask8x8[5+y*8]; 117 | ptr[6] = rune & (0x02ULL << y64) ? ptr[6] : mask8x8[6+y*8]; 118 | ptr[7] = rune & (0x01ULL << y64) ? ptr[7] : mask8x8[7+y*8]; 119 | } else { // NORMAL 120 | ptr[0] = rune & (0x80ULL << y64) ? mask8x8[0+y*8] : ptr[0]; 121 | ptr[1] = rune & (0x40ULL << y64) ? mask8x8[1+y*8] : ptr[1]; 122 | ptr[2] = rune & (0x20ULL << y64) ? mask8x8[2+y*8] : ptr[2]; 123 | ptr[3] = rune & (0x10ULL << y64) ? mask8x8[3+y*8] : ptr[3]; 124 | ptr[4] = rune & (0x08ULL << y64) ? mask8x8[4+y*8] : ptr[4]; 125 | ptr[5] = rune & (0x04ULL << y64) ? mask8x8[5+y*8] : ptr[5]; 126 | ptr[6] = rune & (0x02ULL << y64) ? mask8x8[6+y*8] : ptr[6]; 127 | ptr[7] = rune & (0x01ULL << y64) ? mask8x8[7+y*8] : ptr[7]; 128 | } 129 | ptr += fb.width; 130 | } 131 | ptr -= fb.width * 8; 132 | ptr += 8; 133 | } 134 | 135 | return dim { unsigned(glyphs.size() * 8), unsigned((!glyphs.empty()) * 8) }; 136 | } 137 | 138 | dim render_string( int ox, int oy, int mode, const std::vector &codepoints, const color *mask8x8 = 0 ) const { 139 | std::vector glyphs; 140 | for( auto &cp : codepoints ) { 141 | auto found = PressStart2P_ttf.find(cp); 142 | if( found != PressStart2P_ttf.end() ) { 143 | glyphs.push_back( found->second ); 144 | } else { 145 | glyphs.push_back( 0 ); 146 | } 147 | } 148 | return render_string( ox, oy, mode, glyphs, mask8x8 ); 149 | } 150 | 151 | dim render_string( int ox, int oy, int mode, const char *utf8, const color *mask8x8 = 0 ) const { 152 | auto decode = []( std::vector &codepoints, const char *utf8 ) -> const char * { 153 | uint32_t result, state = 0; 154 | while( *utf8 ) { 155 | // Decode utf8 codepoint a byte at a time. Uses explictly user provided state variable, 156 | // that should be initialized to zero before first use. Places the result to codep. 157 | // Returns UTF8_ACCEPT when a full codepoint achieved 158 | enum { UTF8_ACCEPT = 0, UTF8_REJECT = 1 }; 159 | auto decode_state = []( uint32_t *state, uint32_t *codep, uint32_t byte ) -> uint32_t { 160 | static const uint8_t utf8d[] = { 161 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f 162 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f 163 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f 164 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f 165 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f 166 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf 167 | 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df 168 | 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef 169 | 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff 170 | 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 171 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 172 | 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 173 | 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 174 | 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 175 | }; 176 | uint32_t type = utf8d[byte]; 177 | *codep = (*state != UTF8_ACCEPT) ? (byte & 0x3fu) | (*codep << 6) : (0xff >> type) & (byte); 178 | *state = utf8d[256 + *state*16 + type]; 179 | return *state; 180 | }; 181 | uint32_t res = decode_state( &state, &result, (unsigned char)(*utf8++) ); 182 | /**/ if(res == UTF8_ACCEPT) return codepoints.push_back( result ), utf8; 183 | else if(res == UTF8_REJECT) break; 184 | } 185 | return codepoints.push_back( 0xfffd ), utf8; 186 | }; 187 | std::vector codepoints; 188 | while( *utf8 ) { 189 | utf8 = decode( codepoints, utf8 ); 190 | } 191 | return render_string( ox, oy, mode, codepoints, mask8x8 ); 192 | } 193 | 194 | dim render_glyphs( int ox, int oy, int mode, const color *mask8x8 = 0 ) const { 195 | uint32_t h = (PressStart2P_ttf.size() / 16) + 1; 196 | for( uint32_t y = 0; y < h; y++ ) { 197 | render_line( ox, oy + y*9, ox + 16*9, oy + y*9, P ); 198 | for( uint32_t x = 0; x < 16; x++ ) { 199 | render_line( ox + x*9, oy, ox + x*9, oy + h*9, P ); 200 | auto it = PressStart2P_ttf.cbegin(); 201 | std::advance( it, x + y * 16 ); 202 | render_string( 1 + ox + x*9, 1 + oy + y*9, mode, std::vector { it->first }, mask8x8 ); 203 | } 204 | } 205 | return dim { 16*9, h*9 }; 206 | } 207 | 208 | dim render_string( int ox, int oy, int mode, const std::vector &glyphs, const color &c ) { 209 | return render_string( ox, oy, mode, glyphs, &std::vector( 64, c )[0] ); 210 | } 211 | dim render_string( int ox, int oy, int mode, const std::vector &codepoints, const color &c ){ 212 | return render_string( ox, oy, mode, codepoints, &std::vector( 64, c )[0] ); 213 | } 214 | dim render_string( int ox, int oy, int mode, const char *utf8, const color &c ) { 215 | return render_string( ox, oy, mode, utf8, &std::vector( 64, c )[0] ); 216 | } 217 | dim render_glyphs( int ox, int oy, int mode, const color &c ) { 218 | return render_glyphs( ox, oy, mode, &std::vector( 64, c )[0] ); 219 | } 220 | 221 | /* generated by ttf2mono.cc - https://github.com/r-lyeh */ 222 | #ifdef _MSC_VER 223 | const std::map PressStart2P_ttf = const std::map { 224 | #else 225 | const std::map PressStart2P_ttf { 226 | #endif 227 | {0x0020,0x0000000000000000},{0x0021,0x3838383030003000},{0x0022,0x6c6c6c0000000000}, 228 | {0x0023,0x6cfe6c6c6cfe6c00},{0x0024,0x107cd07c16fc1000},{0x0025,0x62a4c810264a8c00}, 229 | {0x0026,0x70d8d870dacc7e00},{0x0027,0x3030300000000000},{0x0028,0x0c18303030180c00}, 230 | {0x0029,0x6030181818306000},{0x002a,0x006c38fe386c0000},{0x002b,0x0018187e18180000}, 231 | {0x002c,0x0000000000303060},{0x002d,0x0000007e00000000},{0x002e,0x0000000000303000}, 232 | {0x002f,0x0204081020408000},{0x0030,0x384cc6c6c6643800},{0x0031,0x1838181818187e00}, 233 | {0x0032,0x7cc60e3c78e0fe00},{0x0033,0x7e0c183c06c67c00},{0x0034,0x1c3c6cccfe0c0c00}, 234 | {0x0035,0xfcc0fc0606c67c00},{0x0036,0x3c60c0fcc6c67c00},{0x0037,0xfec60c1830303000}, 235 | {0x0038,0x78c4e4789e867c00},{0x0039,0x7cc6c67e060c7800},{0x003a,0x0030300030300000}, 236 | {0x003b,0x0030300030306000},{0x003c,0x0c18306030180c00},{0x003d,0x0000fe00fe000000}, 237 | {0x003e,0x6030180c18306000},{0x003f,0x7cfec60c38003800},{0x0040,0x7c82baaabe807c00}, 238 | {0x0041,0x386cc6c6fec6c600},{0x0042,0xfcc6c6fcc6c6fc00},{0x0043,0x3c66c0c0c0663c00}, 239 | {0x0044,0xf8ccc6c6c6ccf800},{0x0045,0xfec0c0fcc0c0fe00},{0x0046,0xfec0c0fcc0c0c000}, 240 | {0x0047,0x3e60c0cec6663e00},{0x0048,0xc6c6c6fec6c6c600},{0x0049,0x7e18181818187e00}, 241 | {0x004a,0x0606060606c67c00},{0x004b,0xc6ccd8f0f8dcce00},{0x004c,0x6060606060607e00}, 242 | {0x004d,0xc6eefefed6c6c600},{0x004e,0xc6e6f6fedecec600},{0x004f,0x7cc6c6c6c6c67c00}, 243 | {0x0050,0xfcc6c6c6fcc0c000},{0x0051,0x7cc6c6c6decc7a00},{0x0052,0xfcc6c6cef8dcce00}, 244 | {0x0053,0x78ccc07c06c67c00},{0x0054,0x7e18181818181800},{0x0055,0xc6c6c6c6c6c67c00}, 245 | {0x0056,0xc6c6c6ee7c381000},{0x0057,0xc6c6d6fefeeec600},{0x0058,0xc6ee7c387ceec600}, 246 | {0x0059,0x6666663c18181800},{0x005a,0xfe0e1c3870e0fe00},{0x005b,0x3c30303030303c00}, 247 | {0x005c,0x8040201008040200},{0x005d,0x7818181818187800},{0x005e,0x386c000000000000}, 248 | {0x005f,0x00000000000000fe},{0x0060,0x1008000000000000},{0x0061,0x00007c067ec67e00}, 249 | {0x0062,0xc0c0fcc6c6c67c00},{0x0063,0x00007ec0c0c07e00},{0x0064,0x06067ec6c6c67e00}, 250 | {0x0065,0x00007cc6fec07c00},{0x0066,0x0e187e1818181800},{0x0067,0x00007ec6c67e067c}, 251 | {0x0068,0xc0c0fcc6c6c6c600},{0x0069,0x1800381818187e00},{0x006a,0x0c001c0c0c0c0c78}, 252 | {0x006b,0xc0c0cefcf8dcce00},{0x006c,0x3818181818187e00},{0x006d,0x0000fcb6b6b6b600}, 253 | {0x006e,0x0000fcc6c6c6c600},{0x006f,0x00007cc6c6c67c00},{0x0070,0x0000fcc6c6fcc0c0}, 254 | {0x0071,0x00007ec6c67e0606},{0x0072,0x00006e7060606000},{0x0073,0x00007cc07c06fc00}, 255 | {0x0074,0x18187e1818181800},{0x0075,0x0000c6c6c6c67e00},{0x0076,0x00006666663c1800}, 256 | {0x0077,0x0000b6b6b6b67e00},{0x0078,0x0000c6fe38fec600},{0x0079,0x0000c6c6c67e067c}, 257 | {0x007a,0x0000fe1c3870fe00},{0x007b,0x0c18183018180c00},{0x007c,0x1818181818181800}, 258 | {0x007d,0x6030301830306000},{0x007e,0x000070ba1c000000},{0x007f,0x00000000006c6c00}, 259 | {0x00a0,0x0000000000000000},{0x00a1,0x1800181838383800},{0x00a2,0x107cd6d0d67c1000}, 260 | {0x00a3,0x3c6660fc6060fe00},{0x00a4,0x005a2424245a0000},{0x00a5,0x66663c7e187e1800}, 261 | {0x00a6,0x1818180018181800},{0x00a7,0x3c6678241e663c00},{0x00a8,0x6c00000000000000}, 262 | {0x00a9,0x3c4299a1a199423c},{0x00aa,0x783c6c3c00000000},{0x00ab,0x00366cd86c360000}, 263 | {0x00ac,0x00007e0606000000},{0x00ad,0x0000007e00000000},{0x00ae,0x3c42b9a5b9a5423c}, 264 | {0x00af,0x7c00000000000000},{0x00b0,0x1028100000000000},{0x00b1,0x18187e1818007e00}, 265 | {0x00b2,0x380c183c00000000},{0x00b3,0x3c180c3800000000},{0x00b4,0x0810000000000000}, 266 | {0x00b5,0x0000ccccccccf6c0},{0x00b6,0x3e6a4a6a3e0a0a00},{0x00b7,0x0000003030000000}, 267 | {0x00b8,0x0000000000000830},{0x00b9,0x1838183c00000000},{0x00ba,0x386c6c3800000000}, 268 | {0x00bb,0x00d86c366cd80000},{0x00bc,0x42c44852264e8200},{0x00bd,0x42c4485622448e00}, 269 | {0x00be,0xe24428d2264e8200},{0x00bf,0x38003860c6fe7c00},{0x00c0,0x2010386cc6fec600}, 270 | {0x00c1,0x0810386cc6fec600},{0x00c2,0x386c386cc6fec600},{0x00c3,0x3458386cc6fec600}, 271 | {0x00c4,0x6c00386cc6fec600},{0x00c5,0x1028386cc6fec600},{0x00c6,0x3e78d8def8d8de00}, 272 | {0x00c7,0x3c66c0c0663c0830},{0x00c8,0x2010fec0fcc0fe00},{0x00c9,0x0810fec0fcc0fe00}, 273 | {0x00ca,0x386cfec0fcc0fe00},{0x00cb,0x6c00fec0fcc0fe00},{0x00cc,0x10087e1818187e00}, 274 | {0x00cd,0x08107e1818187e00},{0x00ce,0x183c7e1818187e00},{0x00cf,0x66007e1818187e00}, 275 | {0x00d0,0x786c66f6666c7800},{0x00d1,0x3458e6f6fedece00},{0x00d2,0x20107cc6c6c67c00}, 276 | {0x00d3,0x08107cc6c6c67c00},{0x00d4,0x386c7cc6c6c67c00},{0x00d5,0x34587cc6c6c67c00}, 277 | {0x00d6,0x6c007cc6c6c67c00},{0x00d7,0x0044281028440000},{0x00d8,0x7cc6ced6e6c67c00}, 278 | {0x00d9,0x2010c6c6c6c67c00},{0x00da,0x0810c6c6c6c67c00},{0x00db,0x386c00c6c6c67c00}, 279 | {0x00dc,0x6c00c6c6c6c67c00},{0x00dd,0x081066663c181800},{0x00de,0xc0fcc6c6c6fcc000}, 280 | {0x00df,0x3c66666c66766c00},{0x00e0,0x20107c067ec67e00},{0x00e1,0x08107c067ec67e00}, 281 | {0x00e2,0x386c7c067ec67e00},{0x00e3,0x34587c067ec67e00},{0x00e4,0x6c007c067ec67e00}, 282 | {0x00e5,0x10287c067ec67e00},{0x00e6,0x00007c167ed07c00},{0x00e7,0x00007ec0c07e0830}, 283 | {0x00e8,0x20107cc6fec07c00},{0x00e9,0x08107cc6fec07c00},{0x00ea,0x386c7cc6fec07c00}, 284 | {0x00eb,0x6c007cc6fec07c00},{0x00ec,0x2010003818187e00},{0x00ed,0x0810003818187e00}, 285 | {0x00ee,0x386c003818187e00},{0x00ef,0x6c00381818187e00},{0x00f0,0x6478987cc6c67c00}, 286 | {0x00f1,0x3458fcc6c6c6c600},{0x00f2,0x20107cc6c6c67c00},{0x00f3,0x08107cc6c6c67c00}, 287 | {0x00f4,0x386c7cc6c6c67c00},{0x00f5,0x34587cc6c6c67c00},{0x00f6,0x6c007cc6c6c67c00}, 288 | {0x00f7,0x0018007e00180000},{0x00f8,0x00007cced6e67c00},{0x00f9,0x2010c6c6c6c67e00}, 289 | {0x00fa,0x0810c6c6c6c67e00},{0x00fb,0x386c00c6c6c67e00},{0x00fc,0x6c00c6c6c6c67e00}, 290 | {0x00fd,0x0810c6c6c67e067c},{0x00fe,0xc0c0fcc6c6fcc0c0},{0x00ff,0x6c00c6c6c67e067c}, 291 | {0x0100,0x7c00386cc6fec600},{0x0101,0x7c007c067ec67e00},{0x0102,0x4438386cc6fec600}, 292 | {0x0103,0x44387c067ec67e00},{0x0104,0x386cc6fec6040806},{0x0105,0x7c067ec67e102018}, 293 | {0x0106,0x08103c66c0663c00},{0x0107,0x08107ec0c0c07e00},{0x0108,0x386c3c66c0663c00}, 294 | {0x0109,0x386c7ec0c0c07e00},{0x010a,0x18003c66c0663c00},{0x010b,0x18007ec0c0c07e00}, 295 | {0x010c,0x663c3c66c0663c00},{0x010d,0x6c387ec0c0c07e00},{0x010e,0x6c38f8ccc6ccf800}, 296 | {0x010f,0x1b1b7ad8d8d87800},{0x0110,0x786c66f6666c7800},{0x0111,0x0c1e7ccccccc7c00}, 297 | {0x0112,0x7c00fec0fcc0fe00},{0x0113,0x7c007cc6fec07c00},{0x0114,0x4438fec0fcc0fe00}, 298 | {0x0115,0x44387cc6fec07c00},{0x0116,0x1800fec0fcc0fe00},{0x0117,0x18007cc6fec07c00}, 299 | {0x0118,0xfec0fcc0fe102018},{0x0119,0x7cc6fec07c102018},{0x011a,0x6c38fec0fcc0fe00}, 300 | {0x011b,0x6c387cc6fec07c00},{0x011c,0x386c7ec0cec67e00},{0x011d,0x386c7ec6c67e067c}, 301 | {0x011e,0x44387ec0cec67e00},{0x011f,0x44387ec6c67e067c},{0x0120,0x18007ec0cec67e00}, 302 | {0x0121,0x18007ec6c67e067c},{0x0122,0x3e60c0ce663e1830},{0x0123,0x18307ec6c67e067c}, 303 | {0x0124,0x38eec6c6fec6c600},{0x0125,0x38ecc0fcc6c6c600},{0x0126,0xc6fec6fec6c6c600}, 304 | {0x0127,0x60f07c6666666600},{0x0128,0x324c7e1818187e00},{0x0129,0x324c003818187e00}, 305 | {0x012a,0x7e007e1818187e00},{0x012b,0x7c00381818187e00},{0x012c,0x24187e1818187e00}, 306 | {0x012d,0x4438381818187e00},{0x012e,0x7e1818187e102018},{0x012f,0x180038187e102018}, 307 | {0x0130,0x18007e1818187e00},{0x0131,0x0000381818187e00},{0x0132,0xf6666666f6067c00}, 308 | {0x0133,0x6600ee666666063c},{0x0134,0x1c36060606c67c00},{0x0135,0x1c36001c0c0c0c78}, 309 | {0x0136,0xccd8f0f8dcce3060},{0x0137,0xc0cefcf8dcce3060},{0x0138,0x0000c6ccf8ccc600}, 310 | {0x0139,0x0870606060607e00},{0x013a,0x102070303030fc00},{0x013b,0x60606060607e1830}, 311 | {0x013c,0x38181818187e1830},{0x013d,0x6666646060607e00},{0x013e,0x763634303030fc00}, 312 | {0x013f,0x60606c6c60607e00},{0x0140,0x703036363030fc00},{0x0141,0x606070e060607e00}, 313 | {0x0142,0x38181c3818187e00},{0x0143,0x0810e6f6fedece00},{0x0144,0x0810fcc6c6c6c600}, 314 | {0x0145,0xe6f6fedecec61830},{0x0146,0x00fcc6c6c6c61830},{0x0147,0x6c38e6f6fedece00}, 315 | {0x0148,0x6c38fcc6c6c6c600},{0x0149,0xc0c0bc3636363600},{0x014a,0xe6f6fedecec6063c}, 316 | {0x014b,0x00fcc6c6c6c6063c},{0x014c,0x7c007cc6c6c67c00},{0x014d,0x7c007cc6c6c67c00}, 317 | {0x014e,0x44387cc6c6c67c00},{0x014f,0x44387cc6c6c67c00},{0x0150,0x24487cc6c6c67c00}, 318 | {0x0151,0x24487cc6c6c67c00},{0x0152,0x7ed8d8ded8d87e00},{0x0153,0x00007cd6ded07c00}, 319 | {0x0154,0x0810fccef8dcce00},{0x0155,0x08106e7060606000},{0x0156,0xfcc6cef8dcce1830}, 320 | {0x0157,0x006e706060603060},{0x0158,0x2810fccef8dcce00},{0x0159,0x361c6e7060606000}, 321 | {0x015a,0x08107cc07c06fc00},{0x015b,0x08107cc07c06fc00},{0x015c,0x386c7cc07c06fc00}, 322 | {0x015d,0x386c7cc07c06fc00},{0x015e,0x7cc07c06c67c0830},{0x015f,0x007cc07c06fc0830}, 323 | {0x0160,0x6c387cc07c06fc00},{0x0161,0x6c387cc07c06fc00},{0x0162,0x7e18181818180830}, 324 | {0x0163,0x18187e1818180830},{0x0164,0x24187e1818181800},{0x0165,0x2418187e18181800}, 325 | {0x0166,0x7e18181c38181800},{0x0167,0x18187e181c381800},{0x0168,0x3458c6c6c6c67c00}, 326 | {0x0169,0x3458c6c6c6c67e00},{0x016a,0x7c00c6c6c6c67c00},{0x016b,0x7c00c6c6c6c67e00}, 327 | {0x016c,0x4438c6c6c6c67c00},{0x016d,0x4438c6c6c6c67e00},{0x016e,0x386cbac6c6c67c00}, 328 | {0x016f,0x386cbac6c6c67e00},{0x0170,0x2448c6c6c6c67c00},{0x0171,0x2448c6c6c6c67e00}, 329 | {0x0172,0xc6c6c6c67c102018},{0x0173,0xc6c6c6c67e102018},{0x0174,0x386cc6d6fefeee00}, 330 | {0x0175,0x386cb6b6b6b67e00},{0x0176,0x182466663c181800},{0x0177,0x386cc6c6c67e067c}, 331 | {0x0178,0x660066663c181800},{0x0179,0x0810fe1c3870fe00},{0x017a,0x0810fe1c3870fe00}, 332 | {0x017b,0x1800fe1c3870fe00},{0x017c,0x1800fe1c3870fe00},{0x017d,0x6c38fe1c3870fe00}, 333 | {0x017e,0x6c38fe1c3870fe00},{0x017f,0x0e18181818181800},{0x0192,0x0e187e1818187000}, 334 | {0x02c6,0x386c000000000000},{0x02c7,0x6c38000000000000},{0x02c9,0x7c00000000000000}, 335 | {0x02ca,0x0810000000000000},{0x02cb,0x1008000000000000},{0x02d7,0x0000007c00000000}, 336 | {0x02d8,0x4438000000000000},{0x02d9,0x1800000000000000},{0x02da,0x386c380000000000}, 337 | {0x02db,0x0000000000102018},{0x02dc,0x3458000000000000},{0x02dd,0x2448000000000000}, 338 | {0x037a,0x000000000000100c},{0x037e,0x0030300030306000},{0x0384,0x0810000000000000}, 339 | {0x0385,0x4a10000000000000},{0x0386,0x58bc66667e666600},{0x0387,0x0000003030000000}, 340 | {0x0388,0x7fb0303c30303f00},{0x0389,0x73b3333f33333300},{0x038a,0x5e8c0c0c0c0c1e00}, 341 | {0x038c,0x5eb3333333331e00},{0x038e,0x73b3331e0c0c0c00},{0x038f,0x5cb6636363367700}, 342 | {0x0390,0x9420703030301c00},{0x0391,0x386cc6c6fec6c600},{0x0392,0xfcc6c6fcc6c6fc00}, 343 | {0x0393,0x7e60606060606000},{0x0394,0x386cc6c6c6c6fe00},{0x0395,0xfec0c0fcc0c0fe00}, 344 | {0x0396,0xfe0e1c3870e0fe00},{0x0397,0xc6c6c6fec6c6c600},{0x0398,0x7cc6c6fec6c67c00}, 345 | {0x0399,0x7e18181818187e00},{0x039a,0xc6ccd8f0f8dcce00},{0x039b,0x386cc6c6c6c6c600}, 346 | {0x039c,0xc6eefefed6c6c600},{0x039d,0xc6e6f6fedecec600},{0x039e,0xfe00007c0000fe00}, 347 | {0x039f,0x7cc6c6c6c6c67c00},{0x03a0,0xfec6c6c6c6c6c600},{0x03a1,0xfcc6c6c6fcc0c000}, 348 | {0x03a3,0xfe66301c3066fe00},{0x03a4,0x7e18181818181800},{0x03a5,0x6666663c18181800}, 349 | {0x03a6,0x107cd6d6d67c1000},{0x03a7,0xc6ee7c387ceec600},{0x03a8,0xdbdbdb7e18183c00}, 350 | {0x03a9,0x386cc6c6c66cee00},{0x03aa,0x66007e1818187e00},{0x03ab,0x660066663c181800}, 351 | {0x03ac,0x08107ec6c6c67e00},{0x03ad,0x08107ec07cc07e00},{0x03ae,0x0810dc6666666606}, 352 | {0x03af,0x1020703030301c00},{0x03b0,0x9420cc6666663c00},{0x03b1,0x00007ec6c6c67e00}, 353 | {0x03b2,0x7cc6c6fcc6c6fcc0},{0x03b3,0x0000c666663c1818},{0x03b4,0x0e180c7ec6c67c00}, 354 | {0x03b5,0x00007ec07cc07e00},{0x03b6,0x7c3060c0c07c060c},{0x03b7,0x0000dc6666666606}, 355 | {0x03b8,0x386cc6fec66c3800},{0x03b9,0x0000703030301c00},{0x03ba,0x0000c6ccf8ccc600}, 356 | {0x03bb,0xc06030386cc6c600},{0x03bc,0x0000ccccccccf6c0},{0x03bd,0x0000c666663c1800}, 357 | {0x03be,0xfc603860c0780c18},{0x03bf,0x00007cc6c6c67c00},{0x03c0,0x0000fe6c6c6ce600}, 358 | {0x03c1,0x00007cc6c6fcc0c0},{0x03c2,0x00007cc0c07c067c},{0x03c3,0x00007ed8cccc7800}, 359 | {0x03c4,0x00007e1818180e00},{0x03c5,0x0000cc6666663c00},{0x03c6,0x00004cd6d6d67c10}, 360 | {0x03c7,0x0000c66c386cc6c6},{0x03c8,0x0018dbdbdbdb7e18},{0x03c9,0x000044d6d6d66c00}, 361 | {0x03ca,0xd800703030301c00},{0x03cb,0x6c00cc6666663c00},{0x03cc,0x08107cc6c6c67c00}, 362 | {0x03cd,0x0810cc6666663c00},{0x03ce,0x081044d6d6d66c00},{0x0400,0x2010fec0fcc0fe00}, 363 | {0x0401,0x6c00fec0fcc0fe00},{0x0402,0xf8607c666666660c},{0x0403,0x08107e6060606000}, 364 | {0x0404,0x3c66c0fcc0663c00},{0x0405,0x78ccc07c06c67c00},{0x0406,0x7e18181818187e00}, 365 | {0x0407,0x66007e1818187e00},{0x0408,0x0606060606c67c00},{0x0409,0x3070dcd6d6d6dc00}, 366 | {0x040a,0xd0d0dcf6d6d6dc00},{0x040b,0xf8607c6666666600},{0x040c,0x0810c6ccf8ccc600}, 367 | {0x040d,0x2010cedefef6e600},{0x040e,0x4438c6c67e06fc00},{0x040f,0xc6c6c6c6c6c6fe10}, 368 | {0x0410,0x386cc6c6fec6c600},{0x0411,0xfcc0c0fcc6c6fc00},{0x0412,0xfcc6c6fcc6c6fc00}, 369 | {0x0413,0x7e60606060606000},{0x0414,0x1e3636666666fec3},{0x0415,0xfec0c0fcc0c0fe00}, 370 | {0x0416,0xd6d67c387cd6d600},{0x0417,0x7cc6063c06c67c00},{0x0418,0xc6cedefef6e6c600}, 371 | {0x0419,0x4438cedefef6e600},{0x041a,0xc6ccd8f0f8dcce00},{0x041b,0x1e366666c6c6c600}, 372 | {0x041c,0xc6eefefed6c6c600},{0x041d,0xc6c6c6fec6c6c600},{0x041e,0x7cc6c6c6c6c67c00}, 373 | {0x041f,0xfec6c6c6c6c6c600},{0x0420,0xfcc6c6c6fcc0c000},{0x0421,0x3c66c0c0c0663c00}, 374 | {0x0422,0x7e18181818181800},{0x0423,0xc6c6c67e06c67c00},{0x0424,0x107cd6d6d67c1000}, 375 | {0x0425,0xc6ee7c387ceec600},{0x0426,0xccccccccccccfe06},{0x0427,0xc6c6c67e06060600}, 376 | {0x0428,0xd6d6d6d6d6d6fe00},{0x0429,0xd6d6d6d6d6d6ff03},{0x042a,0xe0607c6666667c00}, 377 | {0x042b,0xc6c6f6dededef600},{0x042c,0xc0c0fcc6c6c6fc00},{0x042d,0x78cc067e06cc7800}, 378 | {0x042e,0xcedbdbfbdbdbce00},{0x042f,0x7ec6c6e63e76e600},{0x0430,0x00007c067ec67e00}, 379 | {0x0431,0x7ec0dce6c6c67c00},{0x0432,0x0000fcc6fcc6fc00},{0x0433,0x00007e6060606000}, 380 | {0x0434,0x00007ec6c67e067c},{0x0435,0x00007cc6fec07c00},{0x0436,0x0000d6d67cd6d600}, 381 | {0x0437,0x0000fc067c06fc00},{0x0438,0x0000c6ced6e6c600},{0x0439,0x4438c6ced6e6c600}, 382 | {0x043a,0x0000c6ccf8ccc600},{0x043b,0x00003e6666c6c600},{0x043c,0x0000c6eed6d6c600}, 383 | {0x043d,0x0000c6c6fec6c600},{0x043e,0x00007cc6c6c67c00},{0x043f,0x0000fec6c6c6c600}, 384 | {0x0440,0x0000fcc6c6fcc0c0},{0x0441,0x00007ec0c0c07e00},{0x0442,0x00007e1818181800}, 385 | {0x0443,0x0000c6c6c67e067c},{0x0444,0x00107cd6d6d67c10},{0x0445,0x0000c6fe38fec600}, 386 | {0x0446,0x0000ccccccccfe06},{0x0447,0x0000c6c67e060600},{0x0448,0x0000d6d6d6d6fe00}, 387 | {0x0449,0x0000d6d6d6d6ff03},{0x044a,0x0000e0607c667c00},{0x044b,0x0000c6c6f6def600}, 388 | {0x044c,0x0000c0c0fcc6fc00},{0x044d,0x00007cc61ec67c00},{0x044e,0x0000cedbfbdbce00}, 389 | {0x044f,0x00007ec67ec6c600},{0x0450,0x20107cc6fec07c00},{0x0451,0x6c007cc6fec07c00}, 390 | {0x0452,0x60f8607c6666660c},{0x0453,0x08107e6060606000},{0x0454,0x00007cc6f0c67c00}, 391 | {0x0455,0x00007cc07c06fc00},{0x0456,0x1800381818187e00},{0x0457,0x6c00381818187e00}, 392 | {0x0458,0x0c001c0c0c0c0c78},{0x0459,0x0000307cd6d6dc00},{0x045a,0x0000d0dcf6d6dc00}, 393 | {0x045b,0x60f8607c66666600},{0x045c,0x0810c6ccf8ccc600},{0x045d,0x2010c6ced6e6c600}, 394 | {0x045e,0x4438c6c6c67e067c},{0x045f,0x0000c6c6c6c6fe10},{0x2013,0x000000fe00000000}, 395 | {0x2014,0x000000ff00000000},{0x2015,0x000000fe00000000},{0x2018,0x1830300000000000}, 396 | {0x2019,0x1818300000000000},{0x201a,0x0000000018183000},{0x201c,0x366c6c0000000000}, 397 | {0x201d,0x36366c0000000000},{0x201e,0x000000006c6cd800},{0x2020,0x18187e1818181800}, 398 | {0x2021,0x18187e187e181800},{0x2022,0x0000183c3c180000},{0x2026,0x0000000000929200}, 399 | {0x2030,0x62a4c8102f559e00},{0x2039,0x000c1830180c0000},{0x203a,0x0060301830600000}, 400 | {0x2044,0x0204081020408000},{0x20ac,0x1c36f860f8361c00},{0x20af,0x6090949a9a9aec08}, 401 | {0x2116,0xc4aaaaaaa4a0ae00},{0x2122,0x7e2e2a2a00000000},{0x2190,0x10307fff7f301000}, 402 | {0x2191,0x10387cfe38383838},{0x2192,0x080cfefffe0c0800},{0x2193,0x38383838fe7c3810}, 403 | {0x2202,0x78cc067ec6c67c00},{0x2206,0x386cc6c6c6c6fe00},{0x220f,0xfec6c6c6c6c6c600}, 404 | {0x2211,0xfe66301c3066fe00},{0x221a,0x1e181818d8783800},{0x221e,0x00006c92926c0000}, 405 | {0x222b,0x0c1a181818185830},{0x2248,0x0064980064980000},{0x2260,0x0204fe10fe408000}, 406 | {0x2264,0x0c1830180c003c00},{0x2265,0x30180c1830003c00},{0x25b2,0x101038387c7cfe00}, 407 | {0x25b6,0x80e0f8fef8e08000},{0x25bc,0xfe7c7c3838101000},{0x25c0,0x020e3efe3e0e0200}, 408 | {0x25ca,0x1028448244281000},{0x2605,0x1010fe7c386c4400},{0x2606,0x1010ee44287c4400}, 409 | {0x2660,0x10387cfefe387c00},{0x2663,0x1818666618183c00},{0x2665,0x6cfefefe7c381000}, 410 | {0x2666,0x10387cfe7c381000},{0x266a,0x1018141274f06000},{0xf101,0x00000000001c7c00}, 411 | {0xf8ff,0x0876fdfdfb7e2c00},{0xfb01,0x3860fe6666666600},{0xfb02,0x3e66fe6666666600}}; 412 | }; 413 | --------------------------------------------------------------------------------