├── Anonymous_Pro.ttf ├── .gitignore ├── LICENSE ├── Makefile ├── histogram.c ├── savepng.h ├── savepng_README.md ├── savepng.c ├── framebuffer.c ├── README.md ├── commandhandler.c ├── index.html ├── server.c ├── main.c └── stb_truetype.h /Anonymous_Pro.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/larsmm/pixelflut/HEAD/Anonymous_Pro.ttf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | pixel 31 | 32 | # Debug files 33 | *.dSYM/ 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YOLO LICENSE 2 | 3 | THIS SOFTWARE LICENSE IS PROVIDED "ALL CAPS" SO THAT YOU KNOW IT IS SUPER 4 | SERIOUS AND YOU DON'T MESS AROUND WITH COPYRIGHT LAW BECAUSE YOU WILL GET IN 5 | TROUBLE HERE ARE SOME OTHER BUZZWORDS COMMONLY IN THESE THINGS WARRANTIES 6 | LIABILITY CONTRACT TORT LIABLE CLAIMS RESTRICTION MERCHANTABILITY SUBJECT TO 7 | THE FOLLOWING CONDITIONS: 8 | 9 | 1. foo 10 | 2. bar 11 | 3. foobar 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # ============================= target: all ============================= 2 | 3 | CC = gcc 4 | 5 | LDFLAGS = `pkg-config --libs sdl2` -lm -latomic -ldl -lpthread -lpng -O3 -flto -fomit-frame-pointer 6 | 7 | CFLAGS = -Wall -Wextra -Wno-unused-parameter -pedantic -std=c11 -c `pkg-config --cflags sdl2` -O3 -flto -march=native -fomit-frame-pointer 8 | 9 | # for macos: 10 | UNAME_S := $(shell uname -s) 11 | ifeq ($(UNAME_S),Darwin) 12 | CFLAGS += -framework OpenGL 13 | else 14 | CFLAGS += -lGL 15 | endif 16 | 17 | all: pixelflut 18 | 19 | pixelflut: main.o savepng.o 20 | $(CC) -o pixelflut main.o savepng.o $(LDFLAGS) 21 | 22 | main.o: main.c commandhandler.c framebuffer.c histogram.c server.c 23 | $(CC) $(CFLAGS) $< -o $@ 24 | 25 | savepng.o: savepng.c savepng.h 26 | $(CC) $(CFLAGS) $< -o $@ 27 | 28 | # ============================= clean ============================= 29 | 30 | clean: 31 | rm -rf *.o pixelflut 32 | -------------------------------------------------------------------------------- /histogram.c: -------------------------------------------------------------------------------- 1 | typedef struct 2 | { 3 | uint8_t* index_html; 4 | int index_html_len; 5 | uint32_t buckets[8][8][8]; 6 | } histogram_t; 7 | 8 | static void histogram_init(histogram_t *histogram) 9 | { 10 | const char http_ok[] = "HTTP/1.1 200 OK\r\n\r\n"; 11 | 12 | FILE *file = fopen("index.html", "rt"); 13 | if (!file) 14 | { 15 | fprintf(stderr, "Could not open index.html!\n"); 16 | return; 17 | } 18 | fseek(file, 0, SEEK_END); 19 | long len = ftell(file); 20 | fseek(file, 0, SEEK_SET); 21 | 22 | histogram->index_html_len = len + sizeof(http_ok) - 1; 23 | histogram->index_html = malloc(histogram->index_html_len); 24 | memcpy(histogram->index_html, http_ok, sizeof(http_ok) - 1); 25 | int read = fread(histogram->index_html + sizeof(http_ok) - 1, 1, len, file); 26 | if (!read) 27 | fprintf(stderr, "Could not read index.html!\n"); 28 | fclose(file); 29 | } 30 | 31 | static void histogram_free(histogram_t *histogram) 32 | { 33 | free(histogram->index_html); 34 | histogram->index_html = 0; 35 | } 36 | 37 | static void histogram_update(histogram_t *histogram) 38 | { 39 | uint32_t *buckets = histogram->buckets[0][0]; 40 | for (int i = 0; i < 8 * 8 * 8; i++) 41 | buckets[i] *= 0.99; 42 | } 43 | -------------------------------------------------------------------------------- /savepng.h: -------------------------------------------------------------------------------- 1 | #ifndef _SDL_SAVEPNG 2 | #define _SDL_SAVEPNG 3 | /* 4 | * SDL_SavePNG -- libpng-based SDL_Surface writer. 5 | * 6 | * This code is free software, available under zlib/libpng license. 7 | * http://www.libpng.org/pub/png/src/libpng-LICENSE.txt 8 | */ 9 | #include 10 | 11 | #ifdef __cplusplus 12 | extern "C" { /* This helps CPP projects that include this header */ 13 | #endif 14 | 15 | /* 16 | * Save an SDL_Surface as a PNG file. 17 | * 18 | * Returns 0 success or -1 on failure, the error message is then retrievable 19 | * via SDL_GetError(). 20 | */ 21 | #define SDL_SavePNG(surface, file) \ 22 | SDL_SavePNG_RW(surface, SDL_RWFromFile(file, "wb"), 1) 23 | 24 | /* 25 | * Save an SDL_Surface as a PNG file, using writable RWops. 26 | * 27 | * surface - the SDL_Surface structure containing the image to be saved 28 | * dst - a data stream to save to 29 | * freedst - non-zero to close the stream after being written 30 | * 31 | * Returns 0 success or -1 on failure, the error message is then retrievable 32 | * via SDL_GetError(). 33 | */ 34 | extern int SDL_SavePNG_RW(SDL_Surface *surface, SDL_RWops *rw, int freedst); 35 | 36 | /* 37 | * Return new SDL_Surface with a format suitable for PNG output. 38 | */ 39 | extern SDL_Surface *SDL_PNGFormatAlpha(SDL_Surface *src); 40 | 41 | #ifdef __cplusplus 42 | } /* extern "C" */ 43 | #endif 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /savepng_README.md: -------------------------------------------------------------------------------- 1 | https://github.com/driedfruit/SDL_SavePNG 2 | 3 | 4 | # SDL_SavePNG 5 | 6 | Minimal libpng interface to save SDL_Surfaces as PNG files. 7 | 8 | You might want to take a look in "savepng.h" - it is much shorter and simpler 9 | than this README. 10 | 11 | ## Install 12 | 13 | Add "savepng.c" and "savepng.h" to your project. 14 | 15 | Link the libpng library, i.e. add the `-lpng` LDFLAG (even if you already have 16 | `-lSDL_image`). 17 | 18 | ## Use 19 | 20 | ``` 21 | #include "savepng.h" 22 | 23 | SDL_Surface *bmp = ... //your surface 24 | if (SDL_SavePNG(bmp, "image.png")) { //boring way with error checking 25 | printf("Unable to save png -- %s\n", SDL_GetError()); 26 | } 27 | ``` 28 | 29 | As you can see, `SDL_SavePNG` accepts an SDL_Surface and a filename for it's 30 | input. Similar to SDL_SaveBMP, it is a wrapper around the actual RWops-based 31 | `SDL_SavePNG_RW` function, so you could use that, if needed. 32 | 33 | Lastly, there is `SDL_PNGFormatAlpha`, modeled after SDL_DisplayFormatAlpha, 34 | that would convert *any SDL_Surface* to an *SDL_Surface suitable for PNG 35 | output*. Each call to `SDL_PNGFormatAlpha` produces a **new** SDL_Surface that 36 | **must** be freed using `SDL_FreeSurface`. 37 | 38 | ``` 39 | //safest way, usefull for 'screen' surface 40 | SDL_Surface *tmp = SDL_PNGFormatAlpha(screen); 41 | SDL_SavePNG(tmp, "screenshot.png"); 42 | SDL_FreeSurface(tmp) 43 | ``` 44 | 45 | Such conversion is actually only required for *one* surface format (see below), 46 | and would do **nothing** for all other formats, making it **very fast**. The 47 | format in question is: 48 | 49 | ### 32-bpp surfaces without alpha 50 | 51 | There is a interesting caveat of combining naive libpng and cunning SDL in a 52 | 32-bpp video mode. 53 | 54 | The *screen* surface (obtained by `SDL_SetVideoMode` or similarly) might (and 55 | will!) ignore it's alpha-component even in the 32bpp mode. Meaning that an 56 | 0xAARRGGBB color would be blitted as 0xFFrrggbb irregardless, as if it was a 57 | 24bpp color. 58 | 59 | Since screen itself is never blitted onto anything else, ignoring the alpha 60 | makes perfect sense. However, unlike 24bpp images, the alpha component *does* 61 | exist. Thus, when such surface is saved, it appears to be completely 62 | transparent, as the alpha values for each pixel are set to 0. 63 | 64 | Depending on your video mode, you might or might not need to first convert your 65 | surface using `SDL_PNGFormatAlpha`. If you have absolute control over the video 66 | surface, you can force it to 24bpp (or less) mode, which would avoid the 67 | problem. 68 | 69 | If the surface passed to `SDL_PNGFormatAlpha` is already suitable, a no-op is 70 | performed. It is very fast, so you should probably always convert your surfaces 71 | before saving. 72 | 73 | ### No text chunks 74 | 75 | Unfortunately, a simplistic interface such as SDL_SavePNG provides no means to 76 | write PNG meta-data. If you need to add iTXT chunks to your PNGs, you would 77 | have to modify this code or write your own version. 78 | 79 | If you have some kind of simple API, that would be thematically consistent with 80 | SDL, in mind -- please share. 81 | 82 | ## Demo 83 | 84 | See `main.c` and `Makefile` for an example program. It too is shorter than this 85 | README. 86 | 87 | # About 88 | 89 | The problem in question is very simple, and this little piece of functionality 90 | was implemented and re-implemented multiple times by multiple authors (notably, 91 | Angelo "Encelo" Theodorou and Darren Grant, among others). I decided to write 92 | my own version to ensure it's correctness, learn more about libpng, and to 93 | provide a copy-pastable, maintained, libpng15-aware, palette-supporting 94 | variation that I could link to. You can view it as a continuation of their 95 | efforts. 96 | 97 | SDL_Image would've been perfect place for this, but that library has different 98 | purposes. 99 | 100 | *Next up: code to load SDL_Surfaces as OpenGL 1.1 textures. J/K ;)* 101 | 102 | # Copying 103 | 104 | SDL_SavePNG is available under the zlib/libpng license. -------------------------------------------------------------------------------- /savepng.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SDL_SavePNG -- libpng-based SDL_Surface writer. 3 | * 4 | * This code is free software, available under zlib/libpng license. 5 | * http://www.libpng.org/pub/png/src/libpng-LICENSE.txt 6 | */ 7 | #include 8 | #include 9 | 10 | #define SUCCESS 0 11 | #define ERROR -1 12 | 13 | #define USE_ROW_POINTERS 14 | 15 | #if SDL_BYTEORDER == SDL_BIG_ENDIAN 16 | #define rmask 0xFF000000 17 | #define gmask 0x00FF0000 18 | #define bmask 0x0000FF00 19 | #define amask 0x000000FF 20 | #else 21 | #define rmask 0x000000FF 22 | #define gmask 0x0000FF00 23 | #define bmask 0x00FF0000 24 | #define amask 0xFF000000 25 | #endif 26 | 27 | /* libpng callbacks */ 28 | static void png_error_SDL(png_structp ctx, png_const_charp str) 29 | { 30 | SDL_SetError("libpng: %s\n", str); 31 | } 32 | static void png_write_SDL(png_structp png_ptr, png_bytep data, png_size_t length) 33 | { 34 | SDL_RWops *rw = (SDL_RWops*)png_get_io_ptr(png_ptr); 35 | SDL_RWwrite(rw, data, sizeof(png_byte), length); 36 | } 37 | 38 | SDL_Surface *SDL_PNGFormatAlpha(SDL_Surface *src) 39 | { 40 | SDL_Surface *surf; 41 | SDL_Rect rect = { 0 }; 42 | 43 | /* NO-OP for images < 32bpp and 32bpp images that already have Alpha channel */ 44 | if (src->format->BitsPerPixel <= 24 || src->format->Amask) { 45 | src->refcount++; 46 | return src; 47 | } 48 | 49 | /* Convert 32bpp alpha-less image to 24bpp alpha-less image */ 50 | rect.w = src->w; 51 | rect.h = src->h; 52 | surf = SDL_CreateRGBSurface(src->flags, src->w, src->h, 24, 53 | src->format->Rmask, src->format->Gmask, src->format->Bmask, 0); 54 | SDL_LowerBlit(src, &rect, surf, &rect); 55 | 56 | return surf; 57 | } 58 | 59 | int SDL_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst) 60 | { 61 | png_structp png_ptr; 62 | png_infop info_ptr; 63 | png_colorp pal_ptr; 64 | SDL_Palette *pal; 65 | int i, colortype; 66 | #ifdef USE_ROW_POINTERS 67 | png_bytep *row_pointers; 68 | #endif 69 | /* Initialize and do basic error checking */ 70 | if (!dst) 71 | { 72 | SDL_SetError("Argument 2 to SDL_SavePNG_RW can't be NULL, expecting SDL_RWops*\n"); 73 | return (ERROR); 74 | } 75 | if (!surface) 76 | { 77 | SDL_SetError("Argument 1 to SDL_SavePNG_RW can't be NULL, expecting SDL_Surface*\n"); 78 | if (freedst) SDL_RWclose(dst); 79 | return (ERROR); 80 | } 81 | png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_error_SDL, NULL); /* err_ptr, err_fn, warn_fn */ 82 | if (!png_ptr) 83 | { 84 | SDL_SetError("Unable to png_create_write_struct on %s\n", PNG_LIBPNG_VER_STRING); 85 | if (freedst) SDL_RWclose(dst); 86 | return (ERROR); 87 | } 88 | info_ptr = png_create_info_struct(png_ptr); 89 | if (!info_ptr) 90 | { 91 | SDL_SetError("Unable to png_create_info_struct\n"); 92 | png_destroy_write_struct(&png_ptr, NULL); 93 | if (freedst) SDL_RWclose(dst); 94 | return (ERROR); 95 | } 96 | if (setjmp(png_jmpbuf(png_ptr))) /* All other errors, see also "png_error_SDL" */ 97 | { 98 | png_destroy_write_struct(&png_ptr, &info_ptr); 99 | if (freedst) SDL_RWclose(dst); 100 | return (ERROR); 101 | } 102 | 103 | /* Setup our RWops writer */ 104 | png_set_write_fn(png_ptr, dst, png_write_SDL, NULL); /* w_ptr, write_fn, flush_fn */ 105 | 106 | /* Prepare chunks */ 107 | colortype = PNG_COLOR_MASK_COLOR; 108 | if (surface->format->BytesPerPixel > 0 109 | && surface->format->BytesPerPixel <= 8 110 | && (pal = surface->format->palette)) 111 | { 112 | colortype |= PNG_COLOR_MASK_PALETTE; 113 | pal_ptr = (png_colorp)malloc(pal->ncolors * sizeof(png_color)); 114 | for (i = 0; i < pal->ncolors; i++) { 115 | pal_ptr[i].red = pal->colors[i].r; 116 | pal_ptr[i].green = pal->colors[i].g; 117 | pal_ptr[i].blue = pal->colors[i].b; 118 | } 119 | png_set_PLTE(png_ptr, info_ptr, pal_ptr, pal->ncolors); 120 | free(pal_ptr); 121 | } 122 | else if (surface->format->BytesPerPixel > 3 || surface->format->Amask) 123 | colortype |= PNG_COLOR_MASK_ALPHA; 124 | 125 | png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, 8, colortype, 126 | PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 127 | 128 | // png_set_packing(png_ptr); 129 | 130 | /* Allow BGR surfaces */ 131 | if (surface->format->Rmask == bmask 132 | && surface->format->Gmask == gmask 133 | && surface->format->Bmask == rmask) 134 | png_set_bgr(png_ptr); 135 | 136 | /* Write everything */ 137 | png_write_info(png_ptr, info_ptr); 138 | #ifdef USE_ROW_POINTERS 139 | row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*surface->h); 140 | for (i = 0; i < surface->h; i++) 141 | row_pointers[i] = (png_bytep)(Uint8*)surface->pixels + i * surface->pitch; 142 | png_write_image(png_ptr, row_pointers); 143 | free(row_pointers); 144 | #else 145 | for (i = 0; i < surface->h; i++) 146 | png_write_row(png_ptr, (png_bytep)(Uint8*)surface->pixels + i * surface->pitch); 147 | #endif 148 | png_write_end(png_ptr, info_ptr); 149 | 150 | /* Done */ 151 | png_destroy_write_struct(&png_ptr, &info_ptr); 152 | if (freedst) SDL_RWclose(dst); 153 | return (SUCCESS); 154 | } 155 | -------------------------------------------------------------------------------- /framebuffer.c: -------------------------------------------------------------------------------- 1 | #define STB_TRUETYPE_IMPLEMENTATION 2 | #include "stb_truetype.h" 3 | 4 | typedef struct 5 | { 6 | uint8_t *pixels; 7 | uint32_t width, height; 8 | uint32_t bytesPerPixel; 9 | uint8_t ttf_buffer[1<<25]; 10 | stbtt_fontinfo font; 11 | } framebuffer_t; 12 | 13 | static void framebuffer_init(framebuffer_t *framebuffer, int width, int height, int bytesPerPixel) 14 | { 15 | framebuffer->width = width; 16 | framebuffer->height = height; 17 | framebuffer->bytesPerPixel = bytesPerPixel; 18 | framebuffer->pixels = calloc(width * height, bytesPerPixel); 19 | 20 | FILE *file = fopen("Anonymous_Pro.ttf", "rb"); 21 | if (!file || !fread(framebuffer->ttf_buffer, 1, 1<<25, file)) 22 | { 23 | fprintf(stderr, "ERROR: Could not open font file.\n"); 24 | exit(1); 25 | } 26 | fclose(file); 27 | 28 | stbtt_InitFont(&framebuffer->font, framebuffer->ttf_buffer, stbtt_GetFontOffsetForIndex(framebuffer->ttf_buffer, 0)); 29 | } 30 | 31 | static void framebuffer_free(framebuffer_t *framebuffer) 32 | { 33 | free(framebuffer->pixels); 34 | framebuffer->pixels = 0; 35 | } 36 | 37 | static void framebuffer_fade_out(framebuffer_t *framebuffer) 38 | { 39 | uint8_t *pixel = framebuffer->pixels; 40 | for (uint32_t i = 0; i < framebuffer->width * framebuffer->height; i++) 41 | { 42 | pixel[0] = pixel[0] ? pixel[0] - 1 : pixel[0]; 43 | pixel[1] = pixel[1] ? pixel[1] - 1 : pixel[1]; 44 | pixel[2] = pixel[2] ? pixel[2] - 1 : pixel[2]; 45 | pixel += framebuffer->bytesPerPixel; 46 | } 47 | } 48 | 49 | static void framebuffer_draw_rect( 50 | framebuffer_t *framebuffer, 51 | int x, int y, int w, int h, 52 | uint8_t r, uint8_t g, uint8_t b, uint8_t a) 53 | { 54 | uint32_t na = 255 - a; 55 | uint32_t ar = a * (uint32_t)r; 56 | uint32_t ag = a * (uint32_t)g; 57 | uint32_t ab = a * (uint32_t)b; 58 | for (int cy = y; cy < y + h; cy++) 59 | { 60 | uint8_t *p = framebuffer->pixels + (cy * framebuffer->width + x) * framebuffer->bytesPerPixel; 61 | for (int cx = x; cx < x + w; cx++) 62 | { 63 | p[0] = (uint8_t)((ar + na * (uint32_t)p[0]) >> 8); 64 | p[1] = (uint8_t)((ag + na * (uint32_t)p[1]) >> 8); 65 | p[2] = (uint8_t)((ab + na * (uint32_t)p[2]) >> 8); 66 | p += framebuffer->bytesPerPixel; 67 | } 68 | } 69 | } 70 | 71 | static void framebuffer_measure_text( 72 | framebuffer_t *framebuffer, 73 | char *text, int size, int *w, int *h) 74 | { 75 | float scale = stbtt_ScaleForPixelHeight(&framebuffer->font, size); 76 | int ascent, descent; 77 | stbtt_GetFontVMetrics(&framebuffer->font, &ascent, &descent, 0); 78 | float xpos = 2; 79 | while (*text) 80 | { 81 | int advance, lsb; 82 | stbtt_GetCodepointHMetrics(&framebuffer->font, *text, &advance, &lsb); 83 | xpos += advance * scale; 84 | if (text[1]) 85 | xpos += scale * stbtt_GetCodepointKernAdvance(&framebuffer->font, text[0], text[1]); 86 | text++; 87 | } 88 | *w = xpos + 2; 89 | *h = (int)ceilf(scale * (ascent - descent)); 90 | } 91 | 92 | static void framebuffer_write_text( 93 | framebuffer_t *framebuffer, 94 | int x, int y, char *text, int size, 95 | uint8_t r, uint8_t g, uint8_t b) 96 | { 97 | int screen_w = strlen(text) * size + 10; 98 | int screen_h = size + 10; 99 | uint8_t *screen = alloca(screen_w * screen_h); 100 | memset(screen, 0, screen_w * screen_h); 101 | 102 | float scale = stbtt_ScaleForPixelHeight(&framebuffer->font, size); 103 | int ascent; 104 | stbtt_GetFontVMetrics(&framebuffer->font, &ascent, 0, 0); 105 | int baseline = (int)(ascent * scale); 106 | float xpos = 2; 107 | while (*text) 108 | { 109 | int advance, lsb, x0, y0, x1, y1; 110 | float x_shift = xpos - (float)floor(xpos); 111 | stbtt_GetCodepointHMetrics(&framebuffer->font, *text, &advance, &lsb); 112 | stbtt_GetCodepointBitmapBoxSubpixel(&framebuffer->font, *text, scale, scale, x_shift, 0, &x0, &y0, &x1, &y1); 113 | stbtt_MakeCodepointBitmapSubpixel(&framebuffer->font, &screen[(baseline + y0) * screen_w + (int)xpos + x0], x1 - x0, y1 - y0, screen_w, scale, scale, x_shift, 0, *text); 114 | xpos += advance * scale; 115 | if (text[1]) 116 | xpos += scale * stbtt_GetCodepointKernAdvance(&framebuffer->font, text[0], text[1]); 117 | text++; 118 | } 119 | 120 | for (int cy = y; cy < y + screen_h; cy++) 121 | { 122 | uint8_t *p = framebuffer->pixels + (cy * framebuffer->width + x) * framebuffer->bytesPerPixel; 123 | for (int cx = x; cx < x + screen_w; cx++) 124 | { 125 | uint32_t a = *screen++; 126 | if (a) 127 | { 128 | uint32_t na = 255 - a; 129 | p[0] = (uint8_t)((a * (uint32_t)r + na * (uint32_t)p[0]) >> 8); 130 | p[1] = (uint8_t)((a * (uint32_t)g + na * (uint32_t)p[1]) >> 8); 131 | p[2] = (uint8_t)((a * (uint32_t)b + na * (uint32_t)p[2]) >> 8); 132 | } 133 | p += framebuffer->bytesPerPixel; 134 | } 135 | } 136 | } 137 | 138 | static void framebuffer_write_text_with_background( 139 | framebuffer_t *framebuffer, 140 | int x, int y, char *text, int size, 141 | uint8_t r, uint8_t g, uint8_t b, 142 | uint8_t br, uint8_t bg, uint8_t bb, uint8_t ba) 143 | { 144 | int w, h; 145 | framebuffer_measure_text(framebuffer, text, size, &w, &h); 146 | framebuffer_draw_rect(framebuffer, x, y, w, h, br, bg, bb, ba); 147 | framebuffer_write_text(framebuffer, x, y, text, size, r, g, b); 148 | } 149 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pixelflut 2 | Fast pixelflut server written in C. It is a collaborative coding game. See https://cccgoe.de/wiki/Pixelflut for details about the game itself. In short: project the pixelflut server output onto a wall where many people can see it. Connected clients can then set single pixels by sending a string like "PX [x] [y] [color]\n" (e.g. "PX 100 300 00FF42\n") to its TCP socket. Use netcat, python or whatever you want. 3 | 4 | ## Hardware requirements 5 | Every cpu with a little bit of power (for 2D SDL) should work. On an Core i3-4010U you can easily utilize a 1 GBit Nic. On Raspberry Pi 4B you get around 30 megabytes/sek. On large events, 10 GBit fiber and a few more CPU-Cores are even more fun. On real server hardware you want to add a graphics card. One thread per CPU-Core seems to be a good rule of thumb. 6 | 7 | ## Features 8 | - Multithreaded 9 | - Can display an overlay with some statistics 10 | - Webinterface serves real-time WebGL histogram and help text (same TCP port) 11 | - Optional fade to black for old pixels to encourage pixel refreshes 12 | - Take manual and periodic screenshots 13 | - Supported commands: 14 | - send pixel: 'PX {x} {y} {GG or RRGGBB or RRGGBBAA as HEX}\n' 15 | - set offset for future pixels: 'OFFSET {x} {y}\n' 16 | - request pixel color: 'PX {x} {y}\n' 17 | - request output resolution: 'SIZE\n' 18 | - request client connection count: 'CONNECTIONS\n' 19 | - request help message with all commands: 'HELP\n' 20 | 21 | ## Build 22 | On a clean Debian installation with the "SSH server" and "standard system utilities" selected during setup. A system with desktop should also work. 23 | ``` 24 | apt update 25 | apt install xorg git build-essential pkg-config libsdl2-dev libpng-dev -y 26 | git clone https://github.com/larsmm/pixelflut.git 27 | cd pixelflut 28 | make 29 | ``` 30 | 31 | ## Keys 32 | - q or ctrl+c: quit 33 | - f: toggle fullscreen 34 | - s: take screenshot (png file in pixelflut dir) 35 | 36 | ## Options 37 | ``` 38 | ./pixelflut --help 39 | ``` 40 | ``` 41 | usage: ./pixelflut [OPTION]... 42 | options: 43 | --width Framebuffer width. Default: Screen width. 44 | --height Framebuffer width. Default: Screen height. 45 | --port TCP port. Default: 1234. 46 | --connection_timeout Connection timeout on idle. Default: 5s. 47 | --connections_max Maximum number of open connections. [1-60000] Default: 1000. 48 | --threads Number of connection handler threads. Default: 4. 49 | --no-histogram Disable calculating and serving the histogram over HTTP. 50 | --window Start in window mode. 51 | --fade_out Enable fading out the framebuffer contents. 52 | --fade_interval Interval for fading out the framebuffer as number of displayed frames. Default: 4. 53 | --hide_text Hide the overlay text. 54 | --show_ip_instead_of_hostname Show IPv4 of interface with default-gateway on overlay. 55 | --show_custom_ip Show specific IP instead of hostname. 56 | --screenshot_interval Time between screenshots. (png files in pixelflut dir) Default: disabled. 57 | --screenshot_use_bmp Use bmp instead of png because of speed. 58 | ``` 59 | 60 | ## Start Server locally 61 | If you are using linux without a desktop environment, start x server first: 62 | ``` 63 | startx & # start in background 64 | ``` 65 | Start pixelflut server: 66 | ``` 67 | ./pixelflut 68 | ``` 69 | 70 | ## Start Server over ssh 71 | If you are using linux with a desktop environment it has to run on the same user as you are using to connect over ssh. List availible displays: 72 | ``` 73 | ps e | grep -Po " DISPLAY=[\.0-9A-Za-z:]* " | sort -u 74 | ``` 75 | Start pixelflut on local display (x forwarding), e.g.: 76 | ``` 77 | DISPLAY=localhost:10.0 ./pixelflut --window 78 | ``` 79 | Start pixelflut on server display, e.g.: 80 | ``` 81 | DISPLAY=:0.0 ./pixelflut 82 | ``` 83 | 84 | ## Connection limit 85 | Best practise: set overall limit of the pixelflut-server high (--connections_max 1000) and limit max connections to the pixelflut port (default: 1234) per IP via iptables to 10-20: 86 | ``` 87 | nano iptables.save 88 | ``` 89 | paste (set limit in --connlimit-above): 90 | ``` 91 | *filter 92 | :INPUT ACCEPT [0:0] 93 | :FORWARD ACCEPT [0:0] 94 | :OUTPUT ACCEPT [0:0] 95 | -A INPUT -p tcp -m tcp --dport 1234 --tcp-flags FIN,SYN,RST,ACK SYN -m connlimit --connlimit-above 20 --connlimit-mask 32 --connlimit-saddr -j REJECT 96 | COMMIT 97 | ``` 98 | Ctrl+x, y, return to save. 99 | Activate: 100 | ``` 101 | iptables-restore < iptables.save 102 | ``` 103 | 104 | ## Display driver 105 | Sometimes the free NVidia driver has problems on multiple displays. So install the proprietary driver: 106 | 1. detect the chip and find the right driver: 107 | ``` 108 | nano /etc/sources/sources.list 109 | ``` 110 | Add: "contrib non-free" after each source 111 | ``` 112 | apt install nvidia-detect 113 | nvidia-detect 114 | ``` 115 | 2. install driver: 116 | ``` 117 | apt install for example: nvidia-legacy-340xx-driver 118 | reboot 119 | ``` 120 | 3. configure your displays: 121 | ``` 122 | startx 123 | ``` 124 | run the following inside the x-session and setup your displays: 125 | ``` 126 | nvidia-settings 127 | ``` 128 | or via SSH: 129 | ``` 130 | DISPLAY=:0 nvidia-settings 131 | ``` 132 | maybe restart x server 133 | 134 | ## Prevent standby 135 | If you are using a notebook and want to close the lit. To disable all standby stuff: 136 | ``` 137 | systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target 138 | ``` 139 | 140 | ## TODO 141 | - Use epoll() to check multiple sockets for I/O events at once 142 | - better network-statistics 143 | - ipv6 -------------------------------------------------------------------------------- /commandhandler.c: -------------------------------------------------------------------------------- 1 | typedef int command_status_t; 2 | #define COMMAND_SUCCESS 0 // keeps connection alive for further commands 3 | #define COMMAND_ERROR 1 // can close connection in case of errors 4 | #define COMMAND_CLOSE 2 // always closes connection (after http response etc.) 5 | 6 | static inline char * itoa(int n, char *s) 7 | { 8 | int i, j, l, sign; 9 | char c; 10 | if ((sign = n) < 0) /* record sign */ 11 | n = -n; /* make n positive */ 12 | i = 0; 13 | do { /* generate digits in reverse order */ 14 | s[i++] = n % 10 + '0'; /* get next digit */ 15 | } while ((n /= 10) > 0); /* delete it */ 16 | if (sign < 0) 17 | s[i++] = '-'; 18 | s[i] = '\0'; 19 | l = i; 20 | for (i = 0, j = l-1; i= '0' && *str <= '9') 39 | { 40 | res = res * 10 + (*str - '0'); 41 | ++str; 42 | } 43 | *next = str; 44 | return sign * res; 45 | } 46 | 47 | static command_status_t command_handler(client_connection_t *client, char *cmd) 48 | { 49 | server_t *server = client->server; 50 | framebuffer_t *framebuffer = &server->framebuffer; 51 | if(cmd[0] == 'P' && cmd[1] == 'X' && cmd[2] == ' ') 52 | { 53 | char *pos1 = cmd + 3; 54 | int x = atoi_simple(cmd + 3, &pos1); 55 | if (cmd == pos1) 56 | return COMMAND_ERROR; 57 | char *pos2 = ++pos1; 58 | int y = atoi_simple(pos1, &pos2); 59 | if (pos1 == pos2) 60 | return COMMAND_ERROR; 61 | x += client->offset_x; 62 | y += client->offset_y; 63 | if ((uint32_t)x >= framebuffer->width || (uint32_t)y >= framebuffer->height) 64 | return COMMAND_ERROR; 65 | pos1 = ++pos2; 66 | 67 | uint32_t c = 0; 68 | pos1 = pos2; 69 | for (int i = 0; i < 8; i++) 70 | { 71 | if (*pos1 >= '0' && *pos1 <= '9') 72 | c = c << 4 | (*pos1 - '0'); 73 | else if (*pos1 >= 'a' && *pos1 <= 'f') 74 | c = c << 4 | (*pos1 - 'a' + 10); 75 | else if (*pos1 >= 'A' && *pos1 <= 'F') 76 | c = c << 4 | (*pos1 - 'A' + 10); 77 | else 78 | break; 79 | pos1++; 80 | } 81 | if (pos2 == pos1) // no color specified -> color request 82 | { 83 | char colorout[30]; 84 | uint8_t *pixel = framebuffer->pixels + (y * framebuffer->width + x) * framebuffer->bytesPerPixel; // RGB(A) 85 | snprintf(colorout, sizeof(colorout), "PX %d %d %02x%02x%02x\n", x, y, pixel[0], pixel[1], pixel[2]); 86 | send(client->socket, colorout, strlen(colorout), MSG_DONTWAIT | MSG_NOSIGNAL); 87 | return COMMAND_SUCCESS; 88 | } 89 | 90 | int codelen = pos1 - pos2; 91 | uint8_t r, g, b, a; 92 | if (codelen > 6) { r = c >> 24; g = c >> 16; b = c >> 8; a = c; } else // rgba 93 | if (codelen > 2) { r = c >> 16; g = c >> 8; b = c ; a = 255; } else // rgb 94 | { r = c ; g = c ; b = c ; a = 255; } // gray 95 | 96 | uint8_t *pixel = framebuffer->pixels + (y * framebuffer->width + x) * framebuffer->bytesPerPixel; // RGB(A) 97 | 98 | if (server->flags & SERVER_HISTOGRAM_ENABLED) 99 | server->histogram.buckets[r >> 5][g >> 5][b >> 5]++; // color statistics 100 | 101 | if (a == 255) // fast & usual path 102 | { 103 | pixel[0] = r; 104 | pixel[1] = g; 105 | pixel[2] = b; 106 | } 107 | else 108 | { 109 | int alpha = a * 65793; 110 | int nalpha = (255 - a) * 65793; 111 | pixel[0] = (uint8_t)(r * alpha + pixel[0] * nalpha) >> 16; 112 | pixel[1] = (uint8_t)(g * alpha + pixel[1] * nalpha) >> 16; 113 | pixel[2] = (uint8_t)(b * alpha + pixel[2] * nalpha) >> 16; 114 | } 115 | 116 | atomic_fetch_add(&server->total_pixels_received, 1); 117 | atomic_fetch_add(&server->pixels_per_second_counter, 1); 118 | 119 | return COMMAND_SUCCESS; 120 | } 121 | else if(!strncmp(cmd, "OFFSET ", 7)) 122 | { 123 | int32_t x, y; 124 | if (sscanf(cmd + 7, "%d %d", &x, &y) != 2) 125 | return COMMAND_ERROR; 126 | client->offset_x = x; 127 | client->offset_y = y; 128 | return COMMAND_SUCCESS; 129 | } 130 | else if(!strncmp(cmd, "SIZE", 4)) 131 | { 132 | char out[32]; 133 | int l = sprintf(out, "SIZE %u %u\n", framebuffer->width, framebuffer->height); 134 | send(client->socket, out, l, MSG_DONTWAIT | MSG_NOSIGNAL); 135 | return COMMAND_SUCCESS; 136 | } 137 | else if(!strncmp(cmd, "CONNECTIONS", 11)) 138 | { 139 | char out[32]; 140 | int l = sprintf(out, "CONNECTIONS %d\n", server->connection_count); 141 | send(client->socket, out, l, MSG_DONTWAIT | MSG_NOSIGNAL); 142 | return COMMAND_SUCCESS; 143 | } 144 | else if(!strncmp(cmd, "HELP", 4)) 145 | { 146 | static const char out[] = 147 | "send pixel: 'PX {x} {y} {GG or RRGGBB or RRGGBBAA as HEX}\\n'; " 148 | "set offset for future pixels: 'OFFSET {x} {y}\\n'; " 149 | "request pixel: 'PX {x} {y}\\n'; " 150 | "request resolution: 'SIZE\\n'; " 151 | "request client connection count: 'CONNECTIONS\\n'; " 152 | "request this help message: 'HELP\\n';\n"; 153 | send(client->socket, out, sizeof(out) - 1, MSG_DONTWAIT | MSG_NOSIGNAL); 154 | return COMMAND_SUCCESS; 155 | } 156 | else if(server->flags & SERVER_HISTOGRAM_ENABLED && !strncmp(cmd, "GET", 3)) // obviously totally HTTP compliant! 157 | { 158 | if (!strncmp(cmd + 4, "/data.json", 10)) 159 | { 160 | char out[16384]; 161 | strcpy(out, "HTTP/1.1 200 OK\r\n\r\n["); 162 | char *hp = out + sizeof("HTTP/1.1 200 OK\r\n\r\n[") - 1; 163 | uint32_t *buckets = server->histogram.buckets[0][0]; 164 | for (uint32_t hi = 0; hi < 8 * 8 * 8; hi++) 165 | { 166 | hp = itoa(buckets[hi], hp); 167 | *hp++ = ','; 168 | } 169 | hp[-1] = ']'; 170 | hp[0] = 0; 171 | send(client->socket, out, strlen(out), MSG_DONTWAIT | MSG_NOSIGNAL); 172 | return COMMAND_CLOSE; 173 | } 174 | else 175 | { 176 | send(client->socket, server->histogram.index_html, server->histogram.index_html_len, MSG_DONTWAIT | MSG_NOSIGNAL); 177 | return COMMAND_CLOSE; 178 | } 179 | } 180 | 181 | return COMMAND_ERROR; 182 | } 183 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 180 | 181 | 182 |

ohai to pixelflut!

183 | Pixelflut is a collaborative coding game. Project the pixelflut server output onto a wall where many people can see it. Connected clients can then set single pixels by sending a string like "PX [x] [y] [color]\n" (e.g. "PX 100 300 00FF42\n") to its TCP socket. Use netcat, python or whatever you want.

184 | 185 | Here you get this server: https://github.com/larsmm/pixelflut

186 | 187 |

how to flut

188 | Connect via TCP to this address/port and use the following commandz: 189 |
    190 |
  • send pixel: 'PX {x} {y} {GG or RRGGBB or RRGGBBAA as HEX}\n'
  • 191 |
  • set offset for future pixels: 'OFFSET {x} {y}\n'
  • 192 |
  • request pixel: 'PX {x} {y}\n'
  • 193 |
  • request resolution: 'SIZE\n'
  • 194 |
  • request client connection count: 'CONNECTIONS\n'
  • 195 |
  • request this help message: 'HELP\n'
  • 196 |
197 | Here some code examples: https://cccgoe.de/wiki/Pixelflut

198 | 199 |

color map

200 | Real-time usage of the RGB color space quantized to eight sections on each axis. You can not see the actual pixelflut image via http. This is only a color map of this image. You will find it somewhere on a wall or in jitsi.
201 |
202 | 203 | 204 | -------------------------------------------------------------------------------- /server.c: -------------------------------------------------------------------------------- 1 | #define _DEFAULT_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "framebuffer.c" 18 | #include "histogram.c" 19 | 20 | typedef struct server_t server_t; 21 | 22 | typedef struct 23 | { 24 | server_t *server; 25 | int socket; 26 | atomic_flag lock; 27 | 28 | struct timespec last_msg_time; 29 | 30 | int offset_x, offset_y; 31 | int buffer_used; 32 | char buffer[65536]; 33 | } client_connection_t; 34 | 35 | typedef int server_flags_t; 36 | #define SERVER_NONE 0 37 | #define SERVER_FADE_OUT_ENABLED 1 38 | #define SERVER_HISTOGRAM_ENABLED 2 39 | 40 | struct server_t 41 | { 42 | framebuffer_t framebuffer; 43 | histogram_t histogram; 44 | 45 | uint16_t port; 46 | int socket; 47 | int timeout; 48 | int threads; 49 | server_flags_t flags; 50 | int fade_interval; 51 | 52 | volatile bool running; 53 | int frame; 54 | uint32_t pixels_received_per_second; 55 | struct timespec prev_second; 56 | 57 | pthread_t listen_thread; 58 | pthread_t *client_threads; 59 | client_connection_t *connections; 60 | int connection_capacity; 61 | atomic_uint connection_count; 62 | atomic_uint connection_current; 63 | atomic_uint pixels_per_second_counter; 64 | atomic_uint_fast64_t total_pixels_received; 65 | }; 66 | 67 | #include "commandhandler.c" 68 | 69 | static void server_client_disconnect(client_connection_t *client) 70 | { 71 | int socket = client->socket; 72 | client->socket = 0; 73 | client->offset_x = 0; 74 | client->offset_y = 0; 75 | client->buffer_used = 0; 76 | close(socket); 77 | atomic_fetch_add(&client->server->connection_count, -1); 78 | //printf("client disconnected\n"); 79 | } 80 | 81 | static void server_poll_client_connection(client_connection_t *client, struct timespec *now) 82 | { 83 | if (now->tv_sec - client->last_msg_time.tv_sec >= client->server->timeout) // ignores the nsec for now... 84 | { 85 | printf("server closed connection after timeout\n"); 86 | server_client_disconnect(client); 87 | return; 88 | } 89 | 90 | int read_size = recv(client->socket, client->buffer + client->buffer_used, sizeof(client->buffer) - client->buffer_used , MSG_DONTWAIT); 91 | if(read_size > 0) 92 | { 93 | client->buffer_used += read_size; 94 | 95 | char *start, *end; 96 | start = end = client->buffer; 97 | while (end < client->buffer + client->buffer_used) 98 | { 99 | if (*end == '\n') 100 | { 101 | *end = 0; 102 | command_status_t status = command_handler(client, start); 103 | if (status == COMMAND_SUCCESS) 104 | { 105 | clock_gettime(CLOCK_MONOTONIC, &client->last_msg_time); 106 | } 107 | else if (status == COMMAND_CLOSE) 108 | { 109 | //printf("server closed connection\n"); 110 | server_client_disconnect(client); 111 | return; 112 | } 113 | start = end + 1; 114 | } 115 | end++; 116 | } 117 | 118 | int offset = start - client->buffer; 119 | int count = client->buffer_used - offset; 120 | if (count == client->buffer_used || offset == client->buffer_used) 121 | client->buffer_used = 0; 122 | else if (offset > 0 && count > 0) 123 | { 124 | memmove(client->buffer, start, count); 125 | client->buffer_used -= offset; 126 | } 127 | } 128 | else if (read_size == 0) // = disconnected 129 | { 130 | //printf("client closed connection\n"); 131 | server_client_disconnect(client); 132 | } 133 | else if (errno != EAGAIN && errno != ECONNRESET) 134 | { 135 | fprintf(stderr, "client recv error: %d on socket %d\n", errno, client->socket); 136 | } 137 | } 138 | 139 | static void *server_client_thread(void *param) 140 | { 141 | server_t *server = (server_t*)param; 142 | 143 | struct timespec now; 144 | clock_gettime(CLOCK_MONOTONIC, &now); 145 | int clockSkipCounter = 0; 146 | 147 | while(server->running || server->connection_count) 148 | { 149 | // only update the current time once in a while to save cpu time 150 | if (++clockSkipCounter > 1024) 151 | { 152 | clockSkipCounter = 0; 153 | clock_gettime(CLOCK_MONOTONIC, &now); 154 | } 155 | 156 | unsigned int index = server->connection_current; 157 | while (!atomic_compare_exchange_weak(&server->connection_current, &index, index + 1)) 158 | index = server->connection_current; 159 | 160 | client_connection_t *client = server->connections + index % server->connection_capacity; 161 | if (!atomic_flag_test_and_set(&client->lock)) 162 | { 163 | if (client->socket) 164 | { 165 | if (server->running) 166 | server_poll_client_connection(client, &now); 167 | else 168 | server_client_disconnect(client); 169 | } 170 | atomic_flag_clear(&client->lock); 171 | } 172 | } 173 | return 0; 174 | } 175 | 176 | static void *server_listen_thread(void *param) 177 | { 178 | socklen_t addr_len; 179 | struct sockaddr_in addr; 180 | addr_len = sizeof(addr); 181 | struct timeval tv; 182 | 183 | server_t *server = (server_t*)param; 184 | 185 | printf("Starting Server on port %d...\n", server->port); 186 | server->socket = socket(PF_INET, SOCK_STREAM, 0); 187 | 188 | tv.tv_sec = server->timeout; 189 | tv.tv_usec = 0; 190 | 191 | addr.sin_addr.s_addr = INADDR_ANY; 192 | addr.sin_port = htons(server->port); 193 | addr.sin_family = AF_INET; 194 | 195 | if (server->socket == -1) 196 | { 197 | perror("socket() failed"); 198 | return 0; 199 | } 200 | 201 | if (setsockopt(server->socket, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0) 202 | printf("setsockopt(SO_REUSEADDR) failed\n"); 203 | if (setsockopt(server->socket, SOL_SOCKET, SO_REUSEPORT, &(int){ 1 }, sizeof(int)) < 0) 204 | printf("setsockopt(SO_REUSEPORT) failed\n"); 205 | 206 | int retries; 207 | for (retries = 0; bind(server->socket, (struct sockaddr*)&addr, sizeof(addr)) == -1 && retries < 10; retries++) 208 | { 209 | perror("bind() failed ...retry in 5s"); 210 | usleep(5000000); 211 | } 212 | if (retries == 10) 213 | return 0; 214 | 215 | if (listen(server->socket, 32) == -1) 216 | { 217 | perror("listen() failed"); 218 | return 0; 219 | } 220 | printf("Listening...\n"); 221 | 222 | setsockopt(server->socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(struct timeval)); 223 | setsockopt(server->socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)); 224 | 225 | while (server->running) 226 | { 227 | for (int i = 0; i < server->connection_capacity && server->running; i++) 228 | { 229 | if (!server->connections[i].socket) 230 | { 231 | client_connection_t *client = server->connections + i; 232 | client->server = server; 233 | int client_socket = accept(server->socket, (struct sockaddr*)&addr, &addr_len); 234 | if (client_socket > 0) 235 | { 236 | clock_gettime(CLOCK_MONOTONIC, &client->last_msg_time); 237 | client->socket = client_socket; 238 | atomic_fetch_add(&server->connection_count, 1); 239 | //printf("Client %s connected\n", inet_ntoa(addr.sin_addr)); 240 | } 241 | } 242 | } 243 | } 244 | close(server->socket); 245 | 246 | printf("Ended listening.\n"); 247 | return 0; 248 | } 249 | 250 | static int server_start( 251 | server_t *server, uint16_t port, int max_connections, int timeout, int threads, 252 | int width, int height, int bytesPerPixel, int fade_interval, server_flags_t flags) 253 | { 254 | server->port = port; 255 | server->socket = 0; 256 | server->timeout = timeout; 257 | server->threads = threads; 258 | server->flags = flags; 259 | server->fade_interval = fade_interval; 260 | server->running = true; 261 | server->frame = 0; 262 | 263 | server->connections = calloc(max_connections, sizeof(client_connection_t)); 264 | if (!server->connections) 265 | { 266 | perror("could not allocate max connections"); 267 | server->running = false; 268 | return 0; 269 | } 270 | server->connection_capacity = max_connections; 271 | 272 | assert(fade_interval > 0); 273 | 274 | framebuffer_init(&server->framebuffer, width, height, bytesPerPixel); 275 | 276 | if (flags & SERVER_HISTOGRAM_ENABLED) 277 | histogram_init(&server->histogram); 278 | 279 | if (pthread_create(&server->listen_thread, NULL, server_listen_thread, server) < 0) 280 | { 281 | perror("could not create listen thread"); 282 | server->running = false; 283 | free(server->connections); 284 | framebuffer_free(&server->framebuffer); 285 | return 0; 286 | } 287 | 288 | server->client_threads = calloc(threads, sizeof(pthread_t)); 289 | for (int i = 0; i < threads; i++) 290 | if (pthread_create(&server->client_threads[i], NULL, server_client_thread, server) < 0) 291 | perror("could not create client thread"); 292 | 293 | clock_gettime(CLOCK_MONOTONIC, &server->prev_second); 294 | 295 | return 1; 296 | } 297 | 298 | static void server_stop(server_t *server) 299 | { 300 | server->running = false; 301 | close(server->socket); 302 | 303 | printf("Closing %d client connections ...\n", server->connection_count); 304 | while (server->connection_count) 305 | usleep(100000); 306 | 307 | printf("Joining threads ... "); 308 | pthread_join(server->listen_thread, NULL); 309 | for (int i = 0; i < server->threads; i++) 310 | pthread_join(server->client_threads[i], NULL); 311 | free(server->client_threads); 312 | 313 | printf("Destroying framebuffer...\n"); 314 | framebuffer_free(&server->framebuffer); 315 | 316 | if (server->flags & SERVER_HISTOGRAM_ENABLED) 317 | { 318 | printf("Destroying histogram...\n"); 319 | histogram_free(&server->histogram); 320 | } 321 | 322 | free(server->connections); 323 | } 324 | 325 | static void server_update(server_t *server) 326 | { 327 | if (server->frame % server->fade_interval == 0) 328 | { 329 | if (server->flags & SERVER_FADE_OUT_ENABLED) 330 | framebuffer_fade_out(&server->framebuffer); 331 | if (server->flags & SERVER_HISTOGRAM_ENABLED) 332 | histogram_update(&server->histogram); 333 | } 334 | 335 | struct timespec time; 336 | clock_gettime(CLOCK_MONOTONIC, &time); 337 | float delta = (float)((double)(time.tv_sec - server->prev_second.tv_sec) + 0.000000001 * (double)(time.tv_nsec - server->prev_second.tv_nsec)); 338 | if (delta >= 1.0f) 339 | { 340 | server->prev_second = time; 341 | uint32_t pixels_per_second_counter = server->pixels_per_second_counter; 342 | while (!atomic_compare_exchange_weak(&server->pixels_per_second_counter, &pixels_per_second_counter, 0)) 343 | pixels_per_second_counter = server->pixels_per_second_counter; 344 | server->pixels_received_per_second = pixels_per_second_counter; 345 | } 346 | 347 | server->frame++; 348 | } 349 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "server.c" 2 | #include "savepng.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define BEGINS_WITH(str, test) (!strncmp(str, test, sizeof(test))) 10 | #define OPTION(opt, desc) printf("\t" opt "\t" desc "\n") 11 | 12 | int get_ip_of_default_gateway_interface (char* ip); 13 | void take_screenshot(SDL_Renderer *renderer, long int screenshot_use_bmp); 14 | 15 | int main(int argc, char **argv) 16 | { 17 | int width = 0; 18 | int height = 0; 19 | uint16_t port = 1234; 20 | int connection_timeout = 5; 21 | long unsigned int connections_max = 1000; 22 | int threads = 4; 23 | int serve_histogram = 1; 24 | int start_fullscreen = 1; 25 | int fade_out = 0; 26 | int fade_interval = 4; 27 | int show_text = 1; 28 | int show_ip_instead_of_hostname = 0; 29 | char custom_ip_adress[256] = ""; 30 | long int screenshot_timer = 0, screenshot_interval = 0, screenshot_use_bmp = 0; 31 | 32 | for (int i = 1; i < argc; i++) 33 | { 34 | if (BEGINS_WITH(argv[i], "--help")) 35 | { 36 | printf("usage: %s [OPTION]...\n", argv[0]); 37 | printf("options:\n"); 38 | OPTION("--width ", "\tFramebuffer width. Default: Screen width."); 39 | OPTION("--height ", "\tFramebuffer width. Default: Screen height."); 40 | OPTION("--port ", "\t\tTCP port. Default: 1234."); 41 | OPTION("--connection_timeout ", "Connection timeout on idle. Default: 5s."); 42 | OPTION("--connections_max ", "\tMaximum number of open connections. [1-60000] Default: 1000."); 43 | OPTION("--threads ", "\t\tNumber of connection handler threads. Default: 4."); 44 | OPTION("--no-histogram", "\t\tDisable calculating and serving the histogram over HTTP."); 45 | OPTION("--window", "\t\tStart in window mode."); 46 | OPTION("--fade_out", "\t\tEnable fading out the framebuffer contents."); 47 | OPTION("--fade_interval ", "Interval for fading out the framebuffer as number of displayed frames. Default: 4."); 48 | OPTION("--hide_text", "\t\tHide the overlay text."); 49 | OPTION("--show_ip_instead_of_hostname", "Show IPv4 of interface with default-gateway on overlay."); 50 | OPTION("--show_custom_ip ", "\tShow specific IP instead of hostname."); 51 | OPTION("--screenshot_interval ", "Time between screenshots. (png files in pixelflut dir) Default: disabled."); 52 | OPTION("--screenshot_use_bmp", "\tUse bmp instead of png because of speed."); 53 | return 0; 54 | } 55 | else if (BEGINS_WITH(argv[i], "--width") && i + 1 < argc) 56 | width = strtol(argv[++i], 0, 10); 57 | else if (BEGINS_WITH(argv[i], "--height") && i + 1 < argc) 58 | height = strtol(argv[++i], 0, 10); 59 | else if (BEGINS_WITH(argv[i], "--port") && i + 1 < argc) 60 | port = (uint16_t)strtol(argv[++i], 0, 10); 61 | else if (BEGINS_WITH(argv[i], "--connection_timeout") && i + 1 < argc) 62 | connection_timeout = strtol(argv[++i], 0, 10); 63 | else if (BEGINS_WITH(argv[i], "--connections_max") && i + 1 < argc) 64 | connections_max = strtol(argv[++i], 0, 10); 65 | else if (BEGINS_WITH(argv[i], "--threads") && i + 1 < argc) 66 | threads = strtol(argv[++i], 0, 10); 67 | else if (BEGINS_WITH(argv[i], "--no-histogram")) 68 | serve_histogram = 0; 69 | else if (BEGINS_WITH(argv[i], "--window")) 70 | start_fullscreen = 0; 71 | else if (BEGINS_WITH(argv[i], "--fade_out")) 72 | fade_out = 1; 73 | else if (BEGINS_WITH(argv[i], "--fade_interval") && i + 1 < argc) 74 | fade_interval = strtol(argv[++i], 0, 10); 75 | else if (BEGINS_WITH(argv[i], "--hide_text")) 76 | show_text = 0; 77 | else if (BEGINS_WITH(argv[i], "--show_ip_instead_of_hostname")) 78 | show_ip_instead_of_hostname = 1; 79 | else if (BEGINS_WITH(argv[i], "--show_custom_ip")) 80 | strcpy(custom_ip_adress,argv[++i]); 81 | else if (BEGINS_WITH(argv[i], "--screenshot_interval") && i + 1 < argc) 82 | screenshot_interval = strtol(argv[++i], 0, 10); 83 | else if (BEGINS_WITH(argv[i], "--screenshot_use_bmp")) 84 | screenshot_use_bmp = 1; 85 | else 86 | { 87 | printf("unknown option \"%s\"\n", argv[i]); 88 | return 1; 89 | } 90 | } 91 | 92 | SDL_Init(SDL_INIT_VIDEO); 93 | SDL_ShowCursor(0); 94 | 95 | if (width == 0 || height == 0) 96 | { 97 | SDL_DisplayMode mode; 98 | if (SDL_GetCurrentDisplayMode(0, &mode)) 99 | { 100 | printf("could not retrieve display mode (hint 1: run as root; hint 2: specify the correct xServer: 'DISPLAY=:0.0 ./pixelflut')\n"); 101 | SDL_Quit(); 102 | return 1; 103 | } 104 | 105 | width = start_fullscreen ? mode.w : mode.w - 160; 106 | height = start_fullscreen ? mode.h : mode.h - 160; 107 | } 108 | 109 | if (width < 1 || height < 1) 110 | { 111 | width = 640; 112 | height = 480; 113 | } 114 | 115 | if (connection_timeout < 1) 116 | connection_timeout = 1; 117 | 118 | if (connections_max < 1) 119 | connections_max = 1; 120 | if (connections_max > 60000) 121 | connections_max = 60000; 122 | // set ulimit for this process == maximum number of opened files or sockets or connections: 123 | struct rlimit limit; 124 | limit.rlim_cur = limit.rlim_max = connections_max + 10; 125 | if (setrlimit(RLIMIT_NOFILE, &limit) != 0) printf("Failed to set the maximum number of sockets (ulimit). Error: %d\n", errno); 126 | if (getrlimit(RLIMIT_NOFILE, &limit) != 0) printf("Failed to set the maximum number of sockets (ulimit). Error: %d\n", errno); 127 | if (limit.rlim_cur != connections_max + 10) printf("Failed to set the maximum number of sockets (soft ulimit). Should be %lu, actual value %lu. Fix your system or lower --connections_max\n", connections_max + 10, limit.rlim_cur); 128 | if (limit.rlim_max != connections_max + 10) printf("Failed to set the maximum number of sockets (hard ulimit). Should be %lu, actual value %lu. Fix your system or lower --connections_max\n", connections_max + 10, limit.rlim_max); 129 | // printf("The soft limit is %lu\n", limit.rlim_cur); 130 | // printf("The hard limit is %lu\n", limit.rlim_max); 131 | 132 | if (threads < 1) 133 | threads = 1; 134 | 135 | if (fade_interval < 1) 136 | fade_interval = 1; 137 | 138 | uint32_t window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE; 139 | window_flags |= start_fullscreen ? SDL_WINDOW_FULLSCREEN : 0; 140 | SDL_Window* window = SDL_CreateWindow("pixel", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, window_flags); 141 | SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); 142 | SDL_RenderClear(renderer); 143 | 144 | SDL_Texture* sdlTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, width, height); 145 | const int bytes_per_pixel = 4; 146 | if (!sdlTexture) 147 | { 148 | printf("could not create SDL-texture (hint 1: run as root; hint 2: specify the correct xServer: 'DISPLAY=:0.0 ./pixelflut'; hint 3: if running via ssh, make sure using the same user for ssh and x session)\n"); 149 | SDL_Quit(); 150 | return 1; 151 | } 152 | 153 | server_t *server = calloc(1, sizeof(server_t)); 154 | server_flags_t flags = SERVER_NONE; 155 | flags |= fade_out ? SERVER_FADE_OUT_ENABLED : 0; 156 | flags |= serve_histogram ? SERVER_HISTOGRAM_ENABLED : 0; 157 | if (!server_start( 158 | server, port, connections_max, connection_timeout, threads, 159 | width, height, bytes_per_pixel, fade_interval, flags)) 160 | { 161 | SDL_Quit(); 162 | return 1; 163 | } 164 | 165 | char hostname_or_ip[256] = ""; 166 | char text_additional[256]; 167 | int text_position[2] = { 32, height - 64 }; 168 | int text_size = 20; 169 | uint8_t text_color[3] = { 255, 255, 255 }; 170 | uint8_t text_bgcolor[4] = { 32, 32, 32, 255 }; 171 | 172 | if (show_text) 173 | { 174 | if (show_ip_instead_of_hostname) { 175 | get_ip_of_default_gateway_interface(hostname_or_ip); 176 | } else if (strncmp(custom_ip_adress, "", 256) == 0) { // if custom ip is set 177 | gethostname(hostname_or_ip, sizeof(hostname_or_ip)); 178 | } else { // show hostname (default) 179 | strcpy(hostname_or_ip, custom_ip_adress); 180 | } 181 | 182 | if (serve_histogram) 183 | snprintf(text_additional, sizeof(text_additional), "http://%s:%d", hostname_or_ip, port); 184 | else 185 | snprintf(text_additional, sizeof(text_additional), "echo \"PX \\n\" > nc %s %d", hostname_or_ip, port); 186 | } 187 | 188 | int ctrl = 0; 189 | struct timespec time; 190 | 191 | while("the cat is sleeping in the rc3.world") 192 | { 193 | clock_gettime(CLOCK_MONOTONIC, &time); 194 | 195 | server_update(server); 196 | 197 | if (show_text) 198 | { 199 | framebuffer_write_text_with_background( 200 | &server->framebuffer, 201 | text_position[0], text_position[1], text_additional, text_size, 202 | text_color[0], text_color[1], text_color[2], 203 | text_bgcolor[0], text_bgcolor[1], text_bgcolor[2], text_bgcolor[3]); 204 | 205 | char text[1024]; 206 | sprintf(text, "connections: %4u; Megapixels: %10" PRId64 "; p/s: %8u", 207 | server->connection_count, server->total_pixels_received/1000000, server->pixels_received_per_second); 208 | framebuffer_write_text_with_background( 209 | &server->framebuffer, 210 | text_position[0], text_position[1] + text_size, text, text_size, 211 | text_color[0], text_color[1], text_color[2], 212 | text_bgcolor[0], text_bgcolor[1], text_bgcolor[2], text_bgcolor[3]); 213 | } 214 | 215 | SDL_UpdateTexture(sdlTexture, NULL, server->framebuffer.pixels, server->framebuffer.width * server->framebuffer.bytesPerPixel); 216 | 217 | // in theory SDL_LockTexture should be faster than SDL_UpdateTexture. On rasperry pi 4b i got no visible improvement. 218 | // int pitch=server->framebuffer.width * server->framebuffer.bytesPerPixel; 219 | // SDL_LockTexture(sdlTexture, NULL, (void**)&server->framebuffer.pixels, &pitch); 220 | // SDL_UnlockTexture(sdlTexture); 221 | 222 | SDL_RenderCopy(renderer, sdlTexture, NULL, NULL); 223 | SDL_RenderPresent(renderer); 224 | 225 | SDL_Event event; 226 | if(SDL_PollEvent(&event)) 227 | { 228 | if(event.type == SDL_QUIT) 229 | { 230 | break; 231 | } 232 | if(event.type == SDL_KEYDOWN) 233 | { 234 | if(event.key.keysym.sym == SDLK_RCTRL || event.key.keysym.sym == SDLK_LCTRL) 235 | { 236 | ctrl = 1; 237 | } 238 | if(event.key.keysym.sym == SDLK_q || (ctrl && event.key.keysym.sym == SDLK_c)) 239 | { 240 | break; 241 | } 242 | if(event.key.keysym.sym == SDLK_f) // toggel fullscreen 243 | { 244 | uint32_t flags = SDL_GetWindowFlags(window); 245 | SDL_SetWindowFullscreen(window, (flags & SDL_WINDOW_FULLSCREEN) ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP); 246 | printf("Toggled Fullscreen\n"); 247 | } 248 | if(event.key.keysym.sym == SDLK_s) // take screenshot 249 | { 250 | take_screenshot(renderer, screenshot_use_bmp); 251 | } 252 | } 253 | if(event.type == SDL_KEYUP) 254 | { 255 | if(event.key.keysym.sym == SDLK_RCTRL || event.key.keysym.sym == SDLK_LCTRL) 256 | { 257 | ctrl = 0; 258 | } 259 | } 260 | } 261 | 262 | if ((time.tv_sec - screenshot_timer) > screenshot_interval && screenshot_interval > 0) { // periodic screenshot 263 | screenshot_timer = time.tv_sec; 264 | take_screenshot(renderer, screenshot_use_bmp); 265 | } 266 | } 267 | 268 | exit(0); // TODO: fix hang on shutdown that happens otherwise 269 | 270 | server_stop(server); 271 | free(server); 272 | 273 | SDL_DestroyWindow(window); 274 | SDL_Quit(); 275 | return 0; 276 | } 277 | 278 | void take_screenshot(SDL_Renderer *renderer, long int screenshot_use_bmp) 279 | { 280 | char filename[34]; 281 | int width, height; 282 | SDL_GetRendererOutputSize(renderer, &width, &height); 283 | if (screenshot_use_bmp) { 284 | // if(SDL_SaveBMP(sshot, filename) != 0) 285 | SDL_Surface *sshot = SDL_CreateRGBSurface(0, width, height, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); 286 | SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_RGB888, sshot->pixels, sshot->pitch); 287 | time_t t = time(NULL); 288 | struct tm tm = *localtime(&t); 289 | sprintf(filename, "pixelflut_%d-%02d-%02d_%02d-%02d-%02d.bmp", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); 290 | if(SDL_SaveBMP(sshot, filename) != 0) 291 | printf("Screenshot failed: %s\n", SDL_GetError()); 292 | else 293 | printf("Screenshot taken: %s\n", filename); 294 | SDL_FreeSurface(sshot); 295 | } else { // png 296 | SDL_Surface *sshot = SDL_CreateRGBSurface(0, width, height, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); 297 | SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_ARGB8888, sshot->pixels, sshot->pitch); 298 | SDL_Surface *sshot2 = SDL_PNGFormatAlpha(sshot); 299 | SDL_FreeSurface(sshot); 300 | time_t t = time(NULL); 301 | struct tm tm = *localtime(&t); 302 | sprintf(filename, "pixelflut_%d-%02d-%02d_%02d-%02d-%02d.png", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); 303 | if(SDL_SavePNG(sshot2, filename) != 0) 304 | printf("Screenshot failed: %s\n", SDL_GetError()); 305 | else 306 | printf("Screenshot taken: %s\n", filename); 307 | SDL_FreeSurface(sshot2); 308 | } 309 | } 310 | 311 | int get_ip_of_default_gateway_interface (char* ip) 312 | { 313 | // source: http://www.binarytides.com/get-local-ip-c-linux/ 314 | FILE *f; 315 | char line[100] , *p , *c; 316 | 317 | f = fopen("/proc/net/route" , "r"); 318 | 319 | while(fgets(line , 100 , f)) 320 | { 321 | p = strtok(line , " \t"); 322 | c = strtok(NULL , " \t"); 323 | 324 | if(p!=NULL && c!=NULL) 325 | { 326 | if(strcmp(c , "00000000") == 0) 327 | { 328 | // printf("Default interface is : %s \n" , p); 329 | break; 330 | } 331 | } 332 | } 333 | 334 | //which family do we require , AF_INET or AF_INET6 335 | int fm = AF_INET; 336 | struct ifaddrs *ifaddr, *ifa; 337 | int family , s; 338 | // char ip[NI_MAXHOST]; 339 | 340 | if (getifaddrs(&ifaddr) == -1) 341 | { 342 | perror("getifaddrs"); 343 | exit(EXIT_FAILURE); 344 | } 345 | 346 | //Walk through linked list, maintaining head pointer so we can free list later 347 | for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) 348 | { 349 | if (ifa->ifa_addr == NULL) 350 | { 351 | continue; 352 | } 353 | 354 | family = ifa->ifa_addr->sa_family; 355 | 356 | if(strcmp( ifa->ifa_name , p) == 0) 357 | { 358 | if (family == fm) 359 | { 360 | s = getnameinfo( ifa->ifa_addr, (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6) , ip , NI_MAXHOST , NULL , 0 , NI_NUMERICHOST); 361 | 362 | if (s != 0) 363 | { 364 | printf("Error: can not get IP-Address. (getnameinfo() failed: %s) (quick fix: do not use --show_ip_instead_of_ipname\n", gai_strerror(s)); 365 | exit(EXIT_FAILURE); 366 | } 367 | // printf("address: %s", ip); 368 | return 0; //erfolg 369 | } 370 | // printf("\n"); 371 | } 372 | } 373 | freeifaddrs(ifaddr); 374 | return 0; 375 | } 376 | -------------------------------------------------------------------------------- /stb_truetype.h: -------------------------------------------------------------------------------- 1 | // stb_truetype.h - v1.11 - public domain 2 | // authored from 2009-2015 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 | // Misc other: 25 | // Ryan Gordon 26 | // Simon Glass 27 | // 28 | // Bug/warning reports/fixes: 29 | // "Zer" on mollyrocket (with fix) 30 | // Cass Everitt 31 | // stoiko (Haemimont Games) 32 | // Brian Hook 33 | // Walter van Niftrik 34 | // David Gow 35 | // David Given 36 | // Ivan-Assen Ivanov 37 | // Anthony Pesch 38 | // Johan Duparc 39 | // Hou Qiming 40 | // Fabian "ryg" Giesen 41 | // Martins Mozeiko 42 | // Cap Petschulat 43 | // Omar Cornut 44 | // github:aloucks 45 | // Peter LaValle 46 | // Sergey Popov 47 | // Giumo X. Clanjor 48 | // Higor Euripedes 49 | // Thomas Fields 50 | // Derek Vinyard 51 | // 52 | // VERSION HISTORY 53 | // 54 | // 1.11 (2016-04-02) fix unused-variable warning 55 | // 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef 56 | // 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly 57 | // 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges 58 | // 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; 59 | // variant PackFontRanges to pack and render in separate phases; 60 | // fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); 61 | // fixed an assert() bug in the new rasterizer 62 | // replace assert() with STBTT_assert() in new rasterizer 63 | // 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) 64 | // also more precise AA rasterizer, except if shapes overlap 65 | // remove need for STBTT_sort 66 | // 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC 67 | // 1.04 (2015-04-15) typo in example 68 | // 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes 69 | // 70 | // Full history can be found at the end of this file. 71 | // 72 | // LICENSE 73 | // 74 | // This software is dual-licensed to the public domain and under the following 75 | // license: you are granted a perpetual, irrevocable license to copy, modify, 76 | // publish, and distribute this file as you see fit. 77 | // 78 | // USAGE 79 | // 80 | // Include this file in whatever places neeed to refer to it. In ONE C/C++ 81 | // file, write: 82 | // #define STB_TRUETYPE_IMPLEMENTATION 83 | // before the #include of this file. This expands out the actual 84 | // implementation into that C/C++ file. 85 | // 86 | // To make the implementation private to the file that generates the implementation, 87 | // #define STBTT_STATIC 88 | // 89 | // Simple 3D API (don't ship this, but it's fine for tools and quick start) 90 | // stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture 91 | // stbtt_GetBakedQuad() -- compute quad to draw for a given char 92 | // 93 | // Improved 3D API (more shippable): 94 | // #include "stb_rect_pack.h" -- optional, but you really want it 95 | // stbtt_PackBegin() 96 | // stbtt_PackSetOversample() -- for improved quality on small fonts 97 | // stbtt_PackFontRanges() -- pack and renders 98 | // stbtt_PackEnd() 99 | // stbtt_GetPackedQuad() 100 | // 101 | // "Load" a font file from a memory buffer (you have to keep the buffer loaded) 102 | // stbtt_InitFont() 103 | // stbtt_GetFontOffsetForIndex() -- use for TTC font collections 104 | // 105 | // Render a unicode codepoint to a bitmap 106 | // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap 107 | // stbtt_MakeCodepointBitmap() -- renders into bitmap you provide 108 | // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be 109 | // 110 | // Character advance/positioning 111 | // stbtt_GetCodepointHMetrics() 112 | // stbtt_GetFontVMetrics() 113 | // stbtt_GetCodepointKernAdvance() 114 | // 115 | // Starting with version 1.06, the rasterizer was replaced with a new, 116 | // faster and generally-more-precise rasterizer. The new rasterizer more 117 | // accurately measures pixel coverage for anti-aliasing, except in the case 118 | // where multiple shapes overlap, in which case it overestimates the AA pixel 119 | // coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If 120 | // this turns out to be a problem, you can re-enable the old rasterizer with 121 | // #define STBTT_RASTERIZER_VERSION 1 122 | // which will incur about a 15% speed hit. 123 | // 124 | // ADDITIONAL DOCUMENTATION 125 | // 126 | // Immediately after this block comment are a series of sample programs. 127 | // 128 | // After the sample programs is the "header file" section. This section 129 | // includes documentation for each API function. 130 | // 131 | // Some important concepts to understand to use this library: 132 | // 133 | // Codepoint 134 | // Characters are defined by unicode codepoints, e.g. 65 is 135 | // uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is 136 | // the hiragana for "ma". 137 | // 138 | // Glyph 139 | // A visual character shape (every codepoint is rendered as 140 | // some glyph) 141 | // 142 | // Glyph index 143 | // A font-specific integer ID representing a glyph 144 | // 145 | // Baseline 146 | // Glyph shapes are defined relative to a baseline, which is the 147 | // bottom of uppercase characters. Characters extend both above 148 | // and below the baseline. 149 | // 150 | // Current Point 151 | // As you draw text to the screen, you keep track of a "current point" 152 | // which is the origin of each character. The current point's vertical 153 | // position is the baseline. Even "baked fonts" use this model. 154 | // 155 | // Vertical Font Metrics 156 | // The vertical qualities of the font, used to vertically position 157 | // and space the characters. See docs for stbtt_GetFontVMetrics. 158 | // 159 | // Font Size in Pixels or Points 160 | // The preferred interface for specifying font sizes in stb_truetype 161 | // is to specify how tall the font's vertical extent should be in pixels. 162 | // If that sounds good enough, skip the next paragraph. 163 | // 164 | // Most font APIs instead use "points", which are a common typographic 165 | // measurement for describing font size, defined as 72 points per inch. 166 | // stb_truetype provides a point API for compatibility. However, true 167 | // "per inch" conventions don't make much sense on computer displays 168 | // since they different monitors have different number of pixels per 169 | // inch. For example, Windows traditionally uses a convention that 170 | // there are 96 pixels per inch, thus making 'inch' measurements have 171 | // nothing to do with inches, and thus effectively defining a point to 172 | // be 1.333 pixels. Additionally, the TrueType font data provides 173 | // an explicit scale factor to scale a given font's glyphs to points, 174 | // but the author has observed that this scale factor is often wrong 175 | // for non-commercial fonts, thus making fonts scaled in points 176 | // according to the TrueType spec incoherently sized in practice. 177 | // 178 | // ADVANCED USAGE 179 | // 180 | // Quality: 181 | // 182 | // - Use the functions with Subpixel at the end to allow your characters 183 | // to have subpixel positioning. Since the font is anti-aliased, not 184 | // hinted, this is very import for quality. (This is not possible with 185 | // baked fonts.) 186 | // 187 | // - Kerning is now supported, and if you're supporting subpixel rendering 188 | // then kerning is worth using to give your text a polished look. 189 | // 190 | // Performance: 191 | // 192 | // - Convert Unicode codepoints to glyph indexes and operate on the glyphs; 193 | // if you don't do this, stb_truetype is forced to do the conversion on 194 | // every call. 195 | // 196 | // - There are a lot of memory allocations. We should modify it to take 197 | // a temp buffer and allocate from the temp buffer (without freeing), 198 | // should help performance a lot. 199 | // 200 | // NOTES 201 | // 202 | // The system uses the raw data found in the .ttf file without changing it 203 | // and without building auxiliary data structures. This is a bit inefficient 204 | // on little-endian systems (the data is big-endian), but assuming you're 205 | // caching the bitmaps or glyph shapes this shouldn't be a big deal. 206 | // 207 | // It appears to be very hard to programmatically determine what font a 208 | // given file is in a general way. I provide an API for this, but I don't 209 | // recommend it. 210 | // 211 | // 212 | // SOURCE STATISTICS (based on v0.6c, 2050 LOC) 213 | // 214 | // Documentation & header file 520 LOC \___ 660 LOC documentation 215 | // Sample code 140 LOC / 216 | // Truetype parsing 620 LOC ---- 620 LOC TrueType 217 | // Software rasterization 240 LOC \ . 218 | // Curve tesselation 120 LOC \__ 550 LOC Bitmap creation 219 | // Bitmap management 100 LOC / 220 | // Baked bitmap interface 70 LOC / 221 | // Font name matching & access 150 LOC ---- 150 222 | // C runtime library abstraction 60 LOC ---- 60 223 | // 224 | // 225 | // PERFORMANCE MEASUREMENTS FOR 1.06: 226 | // 227 | // 32-bit 64-bit 228 | // Previous release: 8.83 s 7.68 s 229 | // Pool allocations: 7.72 s 6.34 s 230 | // Inline sort : 6.54 s 5.65 s 231 | // New rasterizer : 5.63 s 5.00 s 232 | 233 | ////////////////////////////////////////////////////////////////////////////// 234 | ////////////////////////////////////////////////////////////////////////////// 235 | //// 236 | //// SAMPLE PROGRAMS 237 | //// 238 | // 239 | // Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless 240 | // 241 | #if 0 242 | #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation 243 | #include "stb_truetype.h" 244 | 245 | unsigned char ttf_buffer[1<<20]; 246 | unsigned char temp_bitmap[512*512]; 247 | 248 | stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs 249 | GLuint ftex; 250 | 251 | void my_stbtt_initfont(void) 252 | { 253 | fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); 254 | stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! 255 | // can free ttf_buffer at this point 256 | glGenTextures(1, &ftex); 257 | glBindTexture(GL_TEXTURE_2D, ftex); 258 | glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); 259 | // can free temp_bitmap at this point 260 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 261 | } 262 | 263 | void my_stbtt_print(float x, float y, char *text) 264 | { 265 | // assume orthographic projection with units = screen pixels, origin at top left 266 | glEnable(GL_TEXTURE_2D); 267 | glBindTexture(GL_TEXTURE_2D, ftex); 268 | glBegin(GL_QUADS); 269 | while (*text) { 270 | if (*text >= 32 && *text < 128) { 271 | stbtt_aligned_quad q; 272 | stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 273 | glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); 274 | glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); 275 | glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); 276 | glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); 277 | } 278 | ++text; 279 | } 280 | glEnd(); 281 | } 282 | #endif 283 | // 284 | // 285 | ////////////////////////////////////////////////////////////////////////////// 286 | // 287 | // Complete program (this compiles): get a single bitmap, print as ASCII art 288 | // 289 | #if 0 290 | #include 291 | #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation 292 | #include "stb_truetype.h" 293 | 294 | char ttf_buffer[1<<25]; 295 | 296 | int main(int argc, char **argv) 297 | { 298 | stbtt_fontinfo font; 299 | unsigned char *bitmap; 300 | int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); 301 | 302 | fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); 303 | 304 | stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); 305 | bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); 306 | 307 | for (j=0; j < h; ++j) { 308 | for (i=0; i < w; ++i) 309 | putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); 310 | putchar('\n'); 311 | } 312 | return 0; 313 | } 314 | #endif 315 | // 316 | // Output: 317 | // 318 | // .ii. 319 | // @@@@@@. 320 | // V@Mio@@o 321 | // :i. V@V 322 | // :oM@@M 323 | // :@@@MM@M 324 | // @@o o@M 325 | // :@@. M@M 326 | // @@@o@@@@ 327 | // :M@@V:@@. 328 | // 329 | ////////////////////////////////////////////////////////////////////////////// 330 | // 331 | // Complete program: print "Hello World!" banner, with bugs 332 | // 333 | #if 0 334 | char buffer[24<<20]; 335 | unsigned char screen[20][79]; 336 | 337 | int main(int arg, char **argv) 338 | { 339 | stbtt_fontinfo font; 340 | int i,j,ascent,baseline,ch=0; 341 | float scale, xpos=2; // leave a little padding in case the character extends left 342 | char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness 343 | 344 | fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); 345 | stbtt_InitFont(&font, buffer, 0); 346 | 347 | scale = stbtt_ScaleForPixelHeight(&font, 15); 348 | stbtt_GetFontVMetrics(&font, &ascent,0,0); 349 | baseline = (int) (ascent*scale); 350 | 351 | while (text[ch]) { 352 | int advance,lsb,x0,y0,x1,y1; 353 | float x_shift = xpos - (float) floor(xpos); 354 | stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); 355 | stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); 356 | stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); 357 | // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong 358 | // because this API is really for baking character bitmaps into textures. if you want to render 359 | // a sequence of characters, you really need to render each bitmap to a temp buffer, then 360 | // "alpha blend" that into the working buffer 361 | xpos += (advance * scale); 362 | if (text[ch+1]) 363 | xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); 364 | ++ch; 365 | } 366 | 367 | for (j=0; j < 20; ++j) { 368 | for (i=0; i < 78; ++i) 369 | putchar(" .:ioVM@"[screen[j][i]>>5]); 370 | putchar('\n'); 371 | } 372 | 373 | return 0; 374 | } 375 | #endif 376 | 377 | 378 | ////////////////////////////////////////////////////////////////////////////// 379 | ////////////////////////////////////////////////////////////////////////////// 380 | //// 381 | //// INTEGRATION WITH YOUR CODEBASE 382 | //// 383 | //// The following sections allow you to supply alternate definitions 384 | //// of C library functions used by stb_truetype. 385 | 386 | #ifdef STB_TRUETYPE_IMPLEMENTATION 387 | // #define your own (u)stbtt_int8/16/32 before including to override this 388 | #ifndef stbtt_uint8 389 | typedef unsigned char stbtt_uint8; 390 | typedef signed char stbtt_int8; 391 | typedef unsigned short stbtt_uint16; 392 | typedef signed short stbtt_int16; 393 | typedef unsigned int stbtt_uint32; 394 | typedef signed int stbtt_int32; 395 | #endif 396 | 397 | typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; 398 | typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; 399 | 400 | // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h 401 | #ifndef STBTT_ifloor 402 | #include 403 | #define STBTT_ifloor(x) ((int) floor(x)) 404 | #define STBTT_iceil(x) ((int) ceil(x)) 405 | #endif 406 | 407 | #ifndef STBTT_sqrt 408 | #include 409 | #define STBTT_sqrt(x) sqrt(x) 410 | #endif 411 | 412 | #ifndef STBTT_fabs 413 | #include 414 | #define STBTT_fabs(x) fabs(x) 415 | #endif 416 | 417 | // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h 418 | #ifndef STBTT_malloc 419 | #include 420 | #define STBTT_malloc(x,u) ((void)(u),malloc(x)) 421 | #define STBTT_free(x,u) ((void)(u),free(x)) 422 | #endif 423 | 424 | #ifndef STBTT_assert 425 | #include 426 | #define STBTT_assert(x) assert(x) 427 | #endif 428 | 429 | #ifndef STBTT_strlen 430 | #include 431 | #define STBTT_strlen(x) strlen(x) 432 | #endif 433 | 434 | #ifndef STBTT_memcpy 435 | #include 436 | #define STBTT_memcpy memcpy 437 | #define STBTT_memset memset 438 | #endif 439 | #endif 440 | 441 | /////////////////////////////////////////////////////////////////////////////// 442 | /////////////////////////////////////////////////////////////////////////////// 443 | //// 444 | //// INTERFACE 445 | //// 446 | //// 447 | 448 | #ifndef __STB_INCLUDE_STB_TRUETYPE_H__ 449 | #define __STB_INCLUDE_STB_TRUETYPE_H__ 450 | 451 | #ifdef STBTT_STATIC 452 | #define STBTT_DEF static 453 | #else 454 | #define STBTT_DEF extern 455 | #endif 456 | 457 | #ifdef __cplusplus 458 | extern "C" { 459 | #endif 460 | 461 | ////////////////////////////////////////////////////////////////////////////// 462 | // 463 | // TEXTURE BAKING API 464 | // 465 | // If you use this API, you only have to call two functions ever. 466 | // 467 | 468 | typedef struct 469 | { 470 | unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap 471 | float xoff,yoff,xadvance; 472 | } stbtt_bakedchar; 473 | 474 | STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) 475 | float pixel_height, // height of font in pixels 476 | unsigned char *pixels, int pw, int ph, // bitmap to be filled in 477 | int first_char, int num_chars, // characters to bake 478 | stbtt_bakedchar *chardata); // you allocate this, it's num_chars long 479 | // if return is positive, the first unused row of the bitmap 480 | // if return is negative, returns the negative of the number of characters that fit 481 | // if return is 0, no characters fit and no rows were used 482 | // This uses a very crappy packing. 483 | 484 | typedef struct 485 | { 486 | float x0,y0,s0,t0; // top-left 487 | float x1,y1,s1,t1; // bottom-right 488 | } stbtt_aligned_quad; 489 | 490 | STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above 491 | int char_index, // character to display 492 | float *xpos, float *ypos, // pointers to current position in screen pixel space 493 | stbtt_aligned_quad *q, // output: quad to draw 494 | int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier 495 | // Call GetBakedQuad with char_index = 'character - first_char', and it 496 | // creates the quad you need to draw and advances the current position. 497 | // 498 | // The coordinate system used assumes y increases downwards. 499 | // 500 | // Characters will extend both above and below the current position; 501 | // see discussion of "BASELINE" above. 502 | // 503 | // It's inefficient; you might want to c&p it and optimize it. 504 | 505 | 506 | 507 | ////////////////////////////////////////////////////////////////////////////// 508 | // 509 | // NEW TEXTURE BAKING API 510 | // 511 | // This provides options for packing multiple fonts into one atlas, not 512 | // perfectly but better than nothing. 513 | 514 | typedef struct 515 | { 516 | unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap 517 | float xoff,yoff,xadvance; 518 | float xoff2,yoff2; 519 | } stbtt_packedchar; 520 | 521 | typedef struct stbtt_pack_context stbtt_pack_context; 522 | typedef struct stbtt_fontinfo stbtt_fontinfo; 523 | #ifndef STB_RECT_PACK_VERSION 524 | typedef struct stbrp_rect stbrp_rect; 525 | #endif 526 | 527 | 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); 528 | // Initializes a packing context stored in the passed-in stbtt_pack_context. 529 | // Future calls using this context will pack characters into the bitmap passed 530 | // in here: a 1-channel bitmap that is weight x height. stride_in_bytes is 531 | // the distance from one row to the next (or 0 to mean they are packed tightly 532 | // together). "padding" is the amount of padding to leave between each 533 | // character (normally you want '1' for bitmaps you'll use as textures with 534 | // bilinear filtering). 535 | // 536 | // Returns 0 on failure, 1 on success. 537 | 538 | STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); 539 | // Cleans up the packing context and frees all memory. 540 | 541 | #define STBTT_POINT_SIZE(x) (-(x)) 542 | 543 | STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, 544 | int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); 545 | // Creates character bitmaps from the font_index'th font found in fontdata (use 546 | // font_index=0 if you don't know what that is). It creates num_chars_in_range 547 | // bitmaps for characters with unicode values starting at first_unicode_char_in_range 548 | // and increasing. Data for how to render them is stored in chardata_for_range; 549 | // pass these to stbtt_GetPackedQuad to get back renderable quads. 550 | // 551 | // font_size is the full height of the character from ascender to descender, 552 | // as computed by stbtt_ScaleForPixelHeight. To use a point size as computed 553 | // by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() 554 | // and pass that result as 'font_size': 555 | // ..., 20 , ... // font max minus min y is 20 pixels tall 556 | // ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall 557 | 558 | typedef struct 559 | { 560 | float font_size; 561 | int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint 562 | int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints 563 | int num_chars; 564 | stbtt_packedchar *chardata_for_range; // output 565 | unsigned char h_oversample, v_oversample; // don't set these, they're used internally 566 | } stbtt_pack_range; 567 | 568 | STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); 569 | // Creates character bitmaps from multiple ranges of characters stored in 570 | // ranges. This will usually create a better-packed bitmap than multiple 571 | // calls to stbtt_PackFontRange. Note that you can call this multiple 572 | // times within a single PackBegin/PackEnd. 573 | 574 | STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); 575 | // Oversampling a font increases the quality by allowing higher-quality subpixel 576 | // positioning, and is especially valuable at smaller text sizes. 577 | // 578 | // This function sets the amount of oversampling for all following calls to 579 | // stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given 580 | // pack context. The default (no oversampling) is achieved by h_oversample=1 581 | // and v_oversample=1. The total number of pixels required is 582 | // h_oversample*v_oversample larger than the default; for example, 2x2 583 | // oversampling requires 4x the storage of 1x1. For best results, render 584 | // oversampled textures with bilinear filtering. Look at the readme in 585 | // stb/tests/oversample for information about oversampled fonts 586 | // 587 | // To use with PackFontRangesGather etc., you must set it before calls 588 | // call to PackFontRangesGatherRects. 589 | 590 | STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above 591 | int char_index, // character to display 592 | float *xpos, float *ypos, // pointers to current position in screen pixel space 593 | stbtt_aligned_quad *q, // output: quad to draw 594 | int align_to_integer); 595 | 596 | STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); 597 | STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); 598 | STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); 599 | // Calling these functions in sequence is roughly equivalent to calling 600 | // stbtt_PackFontRanges(). If you more control over the packing of multiple 601 | // fonts, or if you want to pack custom data into a font texture, take a look 602 | // at the source to of stbtt_PackFontRanges() and create a custom version 603 | // using these functions, e.g. call GatherRects multiple times, 604 | // building up a single array of rects, then call PackRects once, 605 | // then call RenderIntoRects repeatedly. This may result in a 606 | // better packing than calling PackFontRanges multiple times 607 | // (or it may not). 608 | 609 | // this is an opaque structure that you shouldn't mess with which holds 610 | // all the context needed from PackBegin to PackEnd. 611 | struct stbtt_pack_context { 612 | void *user_allocator_context; 613 | void *pack_info; 614 | int width; 615 | int height; 616 | int stride_in_bytes; 617 | int padding; 618 | unsigned int h_oversample, v_oversample; 619 | unsigned char *pixels; 620 | void *nodes; 621 | }; 622 | 623 | ////////////////////////////////////////////////////////////////////////////// 624 | // 625 | // FONT LOADING 626 | // 627 | // 628 | 629 | STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); 630 | // Each .ttf/.ttc file may have more than one font. Each font has a sequential 631 | // index number starting from 0. Call this function to get the font offset for 632 | // a given index; it returns -1 if the index is out of range. A regular .ttf 633 | // file will only define one font and it always be at offset 0, so it will 634 | // return '0' for index 0, and -1 for all other indices. You can just skip 635 | // this step if you know it's that kind of font. 636 | 637 | 638 | // The following structure is defined publically so you can declare one on 639 | // the stack or as a global or etc, but you should treat it as opaque. 640 | struct stbtt_fontinfo 641 | { 642 | void * userdata; 643 | unsigned char * data; // pointer to .ttf file 644 | int fontstart; // offset of start of font 645 | 646 | int numGlyphs; // number of glyphs, needed for range checking 647 | 648 | int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf 649 | int index_map; // a cmap mapping for our chosen character encoding 650 | int indexToLocFormat; // format needed to map from glyph index to glyph 651 | }; 652 | 653 | STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); 654 | // Given an offset into the file that defines a font, this function builds 655 | // the necessary cached info for the rest of the system. You must allocate 656 | // the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't 657 | // need to do anything special to free it, because the contents are pure 658 | // value data with no additional data structures. Returns 0 on failure. 659 | 660 | 661 | ////////////////////////////////////////////////////////////////////////////// 662 | // 663 | // CHARACTER TO GLYPH-INDEX CONVERSIOn 664 | 665 | STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); 666 | // If you're going to perform multiple operations on the same character 667 | // and you want a speed-up, call this function with the character you're 668 | // going to process, then use glyph-based functions instead of the 669 | // codepoint-based functions. 670 | 671 | 672 | ////////////////////////////////////////////////////////////////////////////// 673 | // 674 | // CHARACTER PROPERTIES 675 | // 676 | 677 | STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); 678 | // computes a scale factor to produce a font whose "height" is 'pixels' tall. 679 | // Height is measured as the distance from the highest ascender to the lowest 680 | // descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics 681 | // and computing: 682 | // scale = pixels / (ascent - descent) 683 | // so if you prefer to measure height by the ascent only, use a similar calculation. 684 | 685 | STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); 686 | // computes a scale factor to produce a font whose EM size is mapped to 687 | // 'pixels' tall. This is probably what traditional APIs compute, but 688 | // I'm not positive. 689 | 690 | STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); 691 | // ascent is the coordinate above the baseline the font extends; descent 692 | // is the coordinate below the baseline the font extends (i.e. it is typically negative) 693 | // lineGap is the spacing between one row's descent and the next row's ascent... 694 | // so you should advance the vertical position by "*ascent - *descent + *lineGap" 695 | // these are expressed in unscaled coordinates, so you must multiply by 696 | // the scale factor for a given size 697 | 698 | STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); 699 | // the bounding box around all possible characters 700 | 701 | STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); 702 | // leftSideBearing is the offset from the current horizontal position to the left edge of the character 703 | // advanceWidth is the offset from the current horizontal position to the next horizontal position 704 | // these are expressed in unscaled coordinates 705 | 706 | STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); 707 | // an additional amount to add to the 'advance' value between ch1 and ch2 708 | 709 | STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); 710 | // Gets the bounding box of the visible part of the glyph, in unscaled coordinates 711 | 712 | STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); 713 | STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); 714 | STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); 715 | // as above, but takes one or more glyph indices for greater efficiency 716 | 717 | 718 | ////////////////////////////////////////////////////////////////////////////// 719 | // 720 | // GLYPH SHAPES (you probably don't need these, but they have to go before 721 | // the bitmaps for C declaration-order reasons) 722 | // 723 | 724 | #ifndef STBTT_vmove // you can predefine these to use different values (but why?) 725 | enum { 726 | STBTT_vmove=1, 727 | STBTT_vline, 728 | STBTT_vcurve 729 | }; 730 | #endif 731 | 732 | #ifndef stbtt_vertex // you can predefine this to use different values 733 | // (we share this with other code at RAD) 734 | #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file 735 | typedef struct 736 | { 737 | stbtt_vertex_type x,y,cx,cy; 738 | unsigned char type,padding; 739 | } stbtt_vertex; 740 | #endif 741 | 742 | STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); 743 | // returns non-zero if nothing is drawn for this glyph 744 | 745 | STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); 746 | STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); 747 | // returns # of vertices and fills *vertices with the pointer to them 748 | // these are expressed in "unscaled" coordinates 749 | // 750 | // The shape is a series of countours. Each one starts with 751 | // a STBTT_moveto, then consists of a series of mixed 752 | // STBTT_lineto and STBTT_curveto segments. A lineto 753 | // draws a line from previous endpoint to its x,y; a curveto 754 | // draws a quadratic bezier from previous endpoint to 755 | // its x,y, using cx,cy as the bezier control point. 756 | 757 | STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); 758 | // frees the data allocated above 759 | 760 | ////////////////////////////////////////////////////////////////////////////// 761 | // 762 | // BITMAP RENDERING 763 | // 764 | 765 | STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); 766 | // frees the bitmap allocated below 767 | 768 | 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); 769 | // allocates a large-enough single-channel 8bpp bitmap and renders the 770 | // specified character/glyph at the specified scale into it, with 771 | // antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). 772 | // *width & *height are filled out with the width & height of the bitmap, 773 | // which is stored left-to-right, top-to-bottom. 774 | // 775 | // xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap 776 | 777 | 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); 778 | // the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel 779 | // shift for the character 780 | 781 | 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); 782 | // the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap 783 | // in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap 784 | // is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the 785 | // width and height and positioning info for it first. 786 | 787 | 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); 788 | // same as stbtt_MakeCodepointBitmap, but you can specify a subpixel 789 | // shift for the character 790 | 791 | 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); 792 | // get the bbox of the bitmap centered around the glyph origin; so the 793 | // bitmap width is ix1-ix0, height is iy1-iy0, and location to place 794 | // the bitmap top left is (leftSideBearing*scale,iy0). 795 | // (Note that the bitmap uses y-increases-down, but the shape uses 796 | // y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) 797 | 798 | 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); 799 | // same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel 800 | // shift for the character 801 | 802 | // the following functions are equivalent to the above functions, but operate 803 | // on glyph indices instead of Unicode codepoints (for efficiency) 804 | 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); 805 | 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); 806 | 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); 807 | 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); 808 | 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); 809 | 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); 810 | 811 | 812 | // @TODO: don't expose this structure 813 | typedef struct 814 | { 815 | int w,h,stride; 816 | unsigned char *pixels; 817 | } stbtt__bitmap; 818 | 819 | // rasterize a shape with quadratic beziers into a bitmap 820 | STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into 821 | float flatness_in_pixels, // allowable error of curve in pixels 822 | stbtt_vertex *vertices, // array of vertices defining shape 823 | int num_verts, // number of vertices in above array 824 | float scale_x, float scale_y, // scale applied to input vertices 825 | float shift_x, float shift_y, // translation applied to input vertices 826 | int x_off, int y_off, // another translation applied to input 827 | int invert, // if non-zero, vertically flip shape 828 | void *userdata); // context for to STBTT_MALLOC 829 | 830 | ////////////////////////////////////////////////////////////////////////////// 831 | // 832 | // Finding the right font... 833 | // 834 | // You should really just solve this offline, keep your own tables 835 | // of what font is what, and don't try to get it out of the .ttf file. 836 | // That's because getting it out of the .ttf file is really hard, because 837 | // the names in the file can appear in many possible encodings, in many 838 | // possible languages, and e.g. if you need a case-insensitive comparison, 839 | // the details of that depend on the encoding & language in a complex way 840 | // (actually underspecified in truetype, but also gigantic). 841 | // 842 | // But you can use the provided functions in two possible ways: 843 | // stbtt_FindMatchingFont() will use *case-sensitive* comparisons on 844 | // unicode-encoded names to try to find the font you want; 845 | // you can run this before calling stbtt_InitFont() 846 | // 847 | // stbtt_GetFontNameString() lets you get any of the various strings 848 | // from the file yourself and do your own comparisons on them. 849 | // You have to have called stbtt_InitFont() first. 850 | 851 | 852 | STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); 853 | // returns the offset (not index) of the font that matches, or -1 if none 854 | // if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". 855 | // if you use any other flag, use a font name like "Arial"; this checks 856 | // the 'macStyle' header field; i don't know if fonts set this consistently 857 | #define STBTT_MACSTYLE_DONTCARE 0 858 | #define STBTT_MACSTYLE_BOLD 1 859 | #define STBTT_MACSTYLE_ITALIC 2 860 | #define STBTT_MACSTYLE_UNDERSCORE 4 861 | #define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 862 | 863 | STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); 864 | // returns 1/0 whether the first string interpreted as utf8 is identical to 865 | // the second string interpreted as big-endian utf16... useful for strings from next func 866 | 867 | STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); 868 | // returns the string (which may be big-endian double byte, e.g. for unicode) 869 | // and puts the length in bytes in *length. 870 | // 871 | // some of the values for the IDs are below; for more see the truetype spec: 872 | // http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html 873 | // http://www.microsoft.com/typography/otspec/name.htm 874 | 875 | enum { // platformID 876 | STBTT_PLATFORM_ID_UNICODE =0, 877 | STBTT_PLATFORM_ID_MAC =1, 878 | STBTT_PLATFORM_ID_ISO =2, 879 | STBTT_PLATFORM_ID_MICROSOFT =3 880 | }; 881 | 882 | enum { // encodingID for STBTT_PLATFORM_ID_UNICODE 883 | STBTT_UNICODE_EID_UNICODE_1_0 =0, 884 | STBTT_UNICODE_EID_UNICODE_1_1 =1, 885 | STBTT_UNICODE_EID_ISO_10646 =2, 886 | STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, 887 | STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 888 | }; 889 | 890 | enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT 891 | STBTT_MS_EID_SYMBOL =0, 892 | STBTT_MS_EID_UNICODE_BMP =1, 893 | STBTT_MS_EID_SHIFTJIS =2, 894 | STBTT_MS_EID_UNICODE_FULL =10 895 | }; 896 | 897 | enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes 898 | STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, 899 | STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, 900 | STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, 901 | STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 902 | }; 903 | 904 | enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... 905 | // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs 906 | STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, 907 | STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, 908 | STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, 909 | STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, 910 | STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, 911 | STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D 912 | }; 913 | 914 | enum { // languageID for STBTT_PLATFORM_ID_MAC 915 | STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, 916 | STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, 917 | STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, 918 | STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , 919 | STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , 920 | STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, 921 | STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 922 | }; 923 | 924 | #ifdef __cplusplus 925 | } 926 | #endif 927 | 928 | #endif // __STB_INCLUDE_STB_TRUETYPE_H__ 929 | 930 | /////////////////////////////////////////////////////////////////////////////// 931 | /////////////////////////////////////////////////////////////////////////////// 932 | //// 933 | //// IMPLEMENTATION 934 | //// 935 | //// 936 | 937 | #ifdef STB_TRUETYPE_IMPLEMENTATION 938 | 939 | #ifndef STBTT_MAX_OVERSAMPLE 940 | #define STBTT_MAX_OVERSAMPLE 8 941 | #endif 942 | 943 | #if STBTT_MAX_OVERSAMPLE > 255 944 | #error "STBTT_MAX_OVERSAMPLE cannot be > 255" 945 | #endif 946 | 947 | typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; 948 | 949 | #ifndef STBTT_RASTERIZER_VERSION 950 | #define STBTT_RASTERIZER_VERSION 2 951 | #endif 952 | 953 | #ifdef _MSC_VER 954 | #define STBTT__NOTUSED(v) (void)(v) 955 | #else 956 | #define STBTT__NOTUSED(v) (void)sizeof(v) 957 | #endif 958 | 959 | ////////////////////////////////////////////////////////////////////////// 960 | // 961 | // accessors to parse data from file 962 | // 963 | 964 | // on platforms that don't allow misaligned reads, if we want to allow 965 | // truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE 966 | 967 | #define ttBYTE(p) (* (stbtt_uint8 *) (p)) 968 | #define ttCHAR(p) (* (stbtt_int8 *) (p)) 969 | #define ttFixed(p) ttLONG(p) 970 | 971 | #if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) 972 | 973 | #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) 974 | #define ttSHORT(p) (* (stbtt_int16 *) (p)) 975 | #define ttULONG(p) (* (stbtt_uint32 *) (p)) 976 | #define ttLONG(p) (* (stbtt_int32 *) (p)) 977 | 978 | #else 979 | 980 | static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } 981 | static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } 982 | static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } 983 | static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } 984 | 985 | #endif 986 | 987 | #define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) 988 | #define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) 989 | 990 | static int stbtt__isfont(const stbtt_uint8 *font) 991 | { 992 | // check the version number 993 | if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 994 | if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! 995 | if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF 996 | if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 997 | return 0; 998 | } 999 | 1000 | // @OPTIMIZE: binary search 1001 | static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) 1002 | { 1003 | stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); 1004 | stbtt_uint32 tabledir = fontstart + 12; 1005 | stbtt_int32 i; 1006 | for (i=0; i < num_tables; ++i) { 1007 | stbtt_uint32 loc = tabledir + 16*i; 1008 | if (stbtt_tag(data+loc+0, tag)) 1009 | return ttULONG(data+loc+8); 1010 | } 1011 | return 0; 1012 | } 1013 | 1014 | STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) 1015 | { 1016 | // if it's just a font, there's only one valid index 1017 | if (stbtt__isfont(font_collection)) 1018 | return index == 0 ? 0 : -1; 1019 | 1020 | // check if it's a TTC 1021 | if (stbtt_tag(font_collection, "ttcf")) { 1022 | // version 1? 1023 | if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { 1024 | stbtt_int32 n = ttLONG(font_collection+8); 1025 | if (index >= n) 1026 | return -1; 1027 | return ttULONG(font_collection+12+index*4); 1028 | } 1029 | } 1030 | return -1; 1031 | } 1032 | 1033 | STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) 1034 | { 1035 | stbtt_uint8 *data = (stbtt_uint8 *) data2; 1036 | stbtt_uint32 cmap, t; 1037 | stbtt_int32 i,numTables; 1038 | 1039 | info->data = data; 1040 | info->fontstart = fontstart; 1041 | 1042 | cmap = stbtt__find_table(data, fontstart, "cmap"); // required 1043 | info->loca = stbtt__find_table(data, fontstart, "loca"); // required 1044 | info->head = stbtt__find_table(data, fontstart, "head"); // required 1045 | info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required 1046 | info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required 1047 | info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required 1048 | info->kern = stbtt__find_table(data, fontstart, "kern"); // not required 1049 | if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) 1050 | return 0; 1051 | 1052 | t = stbtt__find_table(data, fontstart, "maxp"); 1053 | if (t) 1054 | info->numGlyphs = ttUSHORT(data+t+4); 1055 | else 1056 | info->numGlyphs = 0xffff; 1057 | 1058 | // find a cmap encoding table we understand *now* to avoid searching 1059 | // later. (todo: could make this installable) 1060 | // the same regardless of glyph. 1061 | numTables = ttUSHORT(data + cmap + 2); 1062 | info->index_map = 0; 1063 | for (i=0; i < numTables; ++i) { 1064 | stbtt_uint32 encoding_record = cmap + 4 + 8 * i; 1065 | // find an encoding we understand: 1066 | switch(ttUSHORT(data+encoding_record)) { 1067 | case STBTT_PLATFORM_ID_MICROSOFT: 1068 | switch (ttUSHORT(data+encoding_record+2)) { 1069 | case STBTT_MS_EID_UNICODE_BMP: 1070 | case STBTT_MS_EID_UNICODE_FULL: 1071 | // MS/Unicode 1072 | info->index_map = cmap + ttULONG(data+encoding_record+4); 1073 | break; 1074 | } 1075 | break; 1076 | case STBTT_PLATFORM_ID_UNICODE: 1077 | // Mac/iOS has these 1078 | // all the encodingIDs are unicode, so we don't bother to check it 1079 | info->index_map = cmap + ttULONG(data+encoding_record+4); 1080 | break; 1081 | } 1082 | } 1083 | if (info->index_map == 0) 1084 | return 0; 1085 | 1086 | info->indexToLocFormat = ttUSHORT(data+info->head + 50); 1087 | return 1; 1088 | } 1089 | 1090 | STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) 1091 | { 1092 | stbtt_uint8 *data = info->data; 1093 | stbtt_uint32 index_map = info->index_map; 1094 | 1095 | stbtt_uint16 format = ttUSHORT(data + index_map + 0); 1096 | if (format == 0) { // apple byte encoding 1097 | stbtt_int32 bytes = ttUSHORT(data + index_map + 2); 1098 | if (unicode_codepoint < bytes-6) 1099 | return ttBYTE(data + index_map + 6 + unicode_codepoint); 1100 | return 0; 1101 | } else if (format == 6) { 1102 | stbtt_uint32 first = ttUSHORT(data + index_map + 6); 1103 | stbtt_uint32 count = ttUSHORT(data + index_map + 8); 1104 | if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) 1105 | return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); 1106 | return 0; 1107 | } else if (format == 2) { 1108 | STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean 1109 | return 0; 1110 | } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges 1111 | stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; 1112 | stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; 1113 | stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); 1114 | stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; 1115 | 1116 | // do a binary search of the segments 1117 | stbtt_uint32 endCount = index_map + 14; 1118 | stbtt_uint32 search = endCount; 1119 | 1120 | if (unicode_codepoint > 0xffff) 1121 | return 0; 1122 | 1123 | // they lie from endCount .. endCount + segCount 1124 | // but searchRange is the nearest power of two, so... 1125 | if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) 1126 | search += rangeShift*2; 1127 | 1128 | // now decrement to bias correctly to find smallest 1129 | search -= 2; 1130 | while (entrySelector) { 1131 | stbtt_uint16 end; 1132 | searchRange >>= 1; 1133 | end = ttUSHORT(data + search + searchRange*2); 1134 | if (unicode_codepoint > end) 1135 | search += searchRange*2; 1136 | --entrySelector; 1137 | } 1138 | search += 2; 1139 | 1140 | { 1141 | stbtt_uint16 offset, start; 1142 | stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); 1143 | 1144 | STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); 1145 | start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); 1146 | if (unicode_codepoint < start) 1147 | return 0; 1148 | 1149 | offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); 1150 | if (offset == 0) 1151 | return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); 1152 | 1153 | return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); 1154 | } 1155 | } else if (format == 12 || format == 13) { 1156 | stbtt_uint32 ngroups = ttULONG(data+index_map+12); 1157 | stbtt_int32 low,high; 1158 | low = 0; high = (stbtt_int32)ngroups; 1159 | // Binary search the right group. 1160 | while (low < high) { 1161 | stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high 1162 | stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); 1163 | stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); 1164 | if ((stbtt_uint32) unicode_codepoint < start_char) 1165 | high = mid; 1166 | else if ((stbtt_uint32) unicode_codepoint > end_char) 1167 | low = mid+1; 1168 | else { 1169 | stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); 1170 | if (format == 12) 1171 | return start_glyph + unicode_codepoint-start_char; 1172 | else // format == 13 1173 | return start_glyph; 1174 | } 1175 | } 1176 | return 0; // not found 1177 | } 1178 | // @TODO 1179 | STBTT_assert(0); 1180 | return 0; 1181 | } 1182 | 1183 | STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) 1184 | { 1185 | return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); 1186 | } 1187 | 1188 | static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) 1189 | { 1190 | v->type = type; 1191 | v->x = (stbtt_int16) x; 1192 | v->y = (stbtt_int16) y; 1193 | v->cx = (stbtt_int16) cx; 1194 | v->cy = (stbtt_int16) cy; 1195 | } 1196 | 1197 | static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) 1198 | { 1199 | int g1,g2; 1200 | 1201 | if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range 1202 | if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format 1203 | 1204 | if (info->indexToLocFormat == 0) { 1205 | g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; 1206 | g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; 1207 | } else { 1208 | g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); 1209 | g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); 1210 | } 1211 | 1212 | return g1==g2 ? -1 : g1; // if length is 0, return -1 1213 | } 1214 | 1215 | STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) 1216 | { 1217 | int g = stbtt__GetGlyfOffset(info, glyph_index); 1218 | if (g < 0) return 0; 1219 | 1220 | if (x0) *x0 = ttSHORT(info->data + g + 2); 1221 | if (y0) *y0 = ttSHORT(info->data + g + 4); 1222 | if (x1) *x1 = ttSHORT(info->data + g + 6); 1223 | if (y1) *y1 = ttSHORT(info->data + g + 8); 1224 | return 1; 1225 | } 1226 | 1227 | STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) 1228 | { 1229 | return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); 1230 | } 1231 | 1232 | STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) 1233 | { 1234 | stbtt_int16 numberOfContours; 1235 | int g = stbtt__GetGlyfOffset(info, glyph_index); 1236 | if (g < 0) return 1; 1237 | numberOfContours = ttSHORT(info->data + g); 1238 | return numberOfContours == 0; 1239 | } 1240 | 1241 | static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, 1242 | stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) 1243 | { 1244 | if (start_off) { 1245 | if (was_off) 1246 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); 1247 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); 1248 | } else { 1249 | if (was_off) 1250 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); 1251 | else 1252 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); 1253 | } 1254 | return num_vertices; 1255 | } 1256 | 1257 | STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) 1258 | { 1259 | stbtt_int16 numberOfContours; 1260 | stbtt_uint8 *endPtsOfContours; 1261 | stbtt_uint8 *data = info->data; 1262 | stbtt_vertex *vertices=0; 1263 | int num_vertices=0; 1264 | int g = stbtt__GetGlyfOffset(info, glyph_index); 1265 | 1266 | *pvertices = NULL; 1267 | 1268 | if (g < 0) return 0; 1269 | 1270 | numberOfContours = ttSHORT(data + g); 1271 | 1272 | if (numberOfContours > 0) { 1273 | stbtt_uint8 flags=0,flagcount; 1274 | stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; 1275 | stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; 1276 | stbtt_uint8 *points; 1277 | endPtsOfContours = (data + g + 10); 1278 | ins = ttUSHORT(data + g + 10 + numberOfContours * 2); 1279 | points = data + g + 10 + numberOfContours * 2 + 2 + ins; 1280 | 1281 | n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); 1282 | 1283 | m = n + 2*numberOfContours; // a loose bound on how many vertices we might need 1284 | vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); 1285 | if (vertices == 0) 1286 | return 0; 1287 | 1288 | next_move = 0; 1289 | flagcount=0; 1290 | 1291 | // in first pass, we load uninterpreted data into the allocated array 1292 | // above, shifted to the end of the array so we won't overwrite it when 1293 | // we create our final data starting from the front 1294 | 1295 | off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated 1296 | 1297 | // first load flags 1298 | 1299 | for (i=0; i < n; ++i) { 1300 | if (flagcount == 0) { 1301 | flags = *points++; 1302 | if (flags & 8) 1303 | flagcount = *points++; 1304 | } else 1305 | --flagcount; 1306 | vertices[off+i].type = flags; 1307 | } 1308 | 1309 | // now load x coordinates 1310 | x=0; 1311 | for (i=0; i < n; ++i) { 1312 | flags = vertices[off+i].type; 1313 | if (flags & 2) { 1314 | stbtt_int16 dx = *points++; 1315 | x += (flags & 16) ? dx : -dx; // ??? 1316 | } else { 1317 | if (!(flags & 16)) { 1318 | x = x + (stbtt_int16) (points[0]*256 + points[1]); 1319 | points += 2; 1320 | } 1321 | } 1322 | vertices[off+i].x = (stbtt_int16) x; 1323 | } 1324 | 1325 | // now load y coordinates 1326 | y=0; 1327 | for (i=0; i < n; ++i) { 1328 | flags = vertices[off+i].type; 1329 | if (flags & 4) { 1330 | stbtt_int16 dy = *points++; 1331 | y += (flags & 32) ? dy : -dy; // ??? 1332 | } else { 1333 | if (!(flags & 32)) { 1334 | y = y + (stbtt_int16) (points[0]*256 + points[1]); 1335 | points += 2; 1336 | } 1337 | } 1338 | vertices[off+i].y = (stbtt_int16) y; 1339 | } 1340 | 1341 | // now convert them to our format 1342 | num_vertices=0; 1343 | sx = sy = cx = cy = scx = scy = 0; 1344 | for (i=0; i < n; ++i) { 1345 | flags = vertices[off+i].type; 1346 | x = (stbtt_int16) vertices[off+i].x; 1347 | y = (stbtt_int16) vertices[off+i].y; 1348 | 1349 | if (next_move == i) { 1350 | if (i != 0) 1351 | num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 1352 | 1353 | // now start the new one 1354 | start_off = !(flags & 1); 1355 | if (start_off) { 1356 | // if we start off with an off-curve point, then when we need to find a point on the curve 1357 | // where we can start, and we need to save some state for when we wraparound. 1358 | scx = x; 1359 | scy = y; 1360 | if (!(vertices[off+i+1].type & 1)) { 1361 | // next point is also a curve point, so interpolate an on-point curve 1362 | sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; 1363 | sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; 1364 | } else { 1365 | // otherwise just use the next point as our start point 1366 | sx = (stbtt_int32) vertices[off+i+1].x; 1367 | sy = (stbtt_int32) vertices[off+i+1].y; 1368 | ++i; // we're using point i+1 as the starting point, so skip it 1369 | } 1370 | } else { 1371 | sx = x; 1372 | sy = y; 1373 | } 1374 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); 1375 | was_off = 0; 1376 | next_move = 1 + ttUSHORT(endPtsOfContours+j*2); 1377 | ++j; 1378 | } else { 1379 | if (!(flags & 1)) { // if it's a curve 1380 | if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint 1381 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); 1382 | cx = x; 1383 | cy = y; 1384 | was_off = 1; 1385 | } else { 1386 | if (was_off) 1387 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); 1388 | else 1389 | stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); 1390 | was_off = 0; 1391 | } 1392 | } 1393 | } 1394 | num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 1395 | } else if (numberOfContours == -1) { 1396 | // Compound shapes. 1397 | int more = 1; 1398 | stbtt_uint8 *comp = data + g + 10; 1399 | num_vertices = 0; 1400 | vertices = 0; 1401 | while (more) { 1402 | stbtt_uint16 flags, gidx; 1403 | int comp_num_verts = 0, i; 1404 | stbtt_vertex *comp_verts = 0, *tmp = 0; 1405 | float mtx[6] = {1,0,0,1,0,0}, m, n; 1406 | 1407 | flags = ttSHORT(comp); comp+=2; 1408 | gidx = ttSHORT(comp); comp+=2; 1409 | 1410 | if (flags & 2) { // XY values 1411 | if (flags & 1) { // shorts 1412 | mtx[4] = ttSHORT(comp); comp+=2; 1413 | mtx[5] = ttSHORT(comp); comp+=2; 1414 | } else { 1415 | mtx[4] = ttCHAR(comp); comp+=1; 1416 | mtx[5] = ttCHAR(comp); comp+=1; 1417 | } 1418 | } 1419 | else { 1420 | // @TODO handle matching point 1421 | STBTT_assert(0); 1422 | } 1423 | if (flags & (1<<3)) { // WE_HAVE_A_SCALE 1424 | mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 1425 | mtx[1] = mtx[2] = 0; 1426 | } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE 1427 | mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; 1428 | mtx[1] = mtx[2] = 0; 1429 | mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 1430 | } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO 1431 | mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; 1432 | mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; 1433 | mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; 1434 | mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 1435 | } 1436 | 1437 | // Find transformation scales. 1438 | m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); 1439 | n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); 1440 | 1441 | // Get indexed glyph. 1442 | comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); 1443 | if (comp_num_verts > 0) { 1444 | // Transform vertices. 1445 | for (i = 0; i < comp_num_verts; ++i) { 1446 | stbtt_vertex* v = &comp_verts[i]; 1447 | stbtt_vertex_type x,y; 1448 | x=v->x; y=v->y; 1449 | v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); 1450 | v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); 1451 | x=v->cx; y=v->cy; 1452 | v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); 1453 | v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); 1454 | } 1455 | // Append vertices. 1456 | tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); 1457 | if (!tmp) { 1458 | if (vertices) STBTT_free(vertices, info->userdata); 1459 | if (comp_verts) STBTT_free(comp_verts, info->userdata); 1460 | return 0; 1461 | } 1462 | if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); 1463 | STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); 1464 | if (vertices) STBTT_free(vertices, info->userdata); 1465 | vertices = tmp; 1466 | STBTT_free(comp_verts, info->userdata); 1467 | num_vertices += comp_num_verts; 1468 | } 1469 | // More components ? 1470 | more = flags & (1<<5); 1471 | } 1472 | } else if (numberOfContours < 0) { 1473 | // @TODO other compound variations? 1474 | STBTT_assert(0); 1475 | } else { 1476 | // numberOfCounters == 0, do nothing 1477 | } 1478 | 1479 | *pvertices = vertices; 1480 | return num_vertices; 1481 | } 1482 | 1483 | STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) 1484 | { 1485 | stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); 1486 | if (glyph_index < numOfLongHorMetrics) { 1487 | if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); 1488 | if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); 1489 | } else { 1490 | if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); 1491 | if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); 1492 | } 1493 | } 1494 | 1495 | STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) 1496 | { 1497 | stbtt_uint8 *data = info->data + info->kern; 1498 | stbtt_uint32 needle, straw; 1499 | int l, r, m; 1500 | 1501 | // we only look at the first table. it must be 'horizontal' and format 0. 1502 | if (!info->kern) 1503 | return 0; 1504 | if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 1505 | return 0; 1506 | if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format 1507 | return 0; 1508 | 1509 | l = 0; 1510 | r = ttUSHORT(data+10) - 1; 1511 | needle = glyph1 << 16 | glyph2; 1512 | while (l <= r) { 1513 | m = (l + r) >> 1; 1514 | straw = ttULONG(data+18+(m*6)); // note: unaligned read 1515 | if (needle < straw) 1516 | r = m - 1; 1517 | else if (needle > straw) 1518 | l = m + 1; 1519 | else 1520 | return ttSHORT(data+22+(m*6)); 1521 | } 1522 | return 0; 1523 | } 1524 | 1525 | STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) 1526 | { 1527 | if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs 1528 | return 0; 1529 | return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); 1530 | } 1531 | 1532 | STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) 1533 | { 1534 | stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); 1535 | } 1536 | 1537 | STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) 1538 | { 1539 | if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); 1540 | if (descent) *descent = ttSHORT(info->data+info->hhea + 6); 1541 | if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); 1542 | } 1543 | 1544 | STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) 1545 | { 1546 | *x0 = ttSHORT(info->data + info->head + 36); 1547 | *y0 = ttSHORT(info->data + info->head + 38); 1548 | *x1 = ttSHORT(info->data + info->head + 40); 1549 | *y1 = ttSHORT(info->data + info->head + 42); 1550 | } 1551 | 1552 | STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) 1553 | { 1554 | int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); 1555 | return (float) height / fheight; 1556 | } 1557 | 1558 | STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) 1559 | { 1560 | int unitsPerEm = ttUSHORT(info->data + info->head + 18); 1561 | return pixels / unitsPerEm; 1562 | } 1563 | 1564 | STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) 1565 | { 1566 | STBTT_free(v, info->userdata); 1567 | } 1568 | 1569 | ////////////////////////////////////////////////////////////////////////////// 1570 | // 1571 | // antialiasing software rasterizer 1572 | // 1573 | 1574 | 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) 1575 | { 1576 | int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning 1577 | if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { 1578 | // e.g. space character 1579 | if (ix0) *ix0 = 0; 1580 | if (iy0) *iy0 = 0; 1581 | if (ix1) *ix1 = 0; 1582 | if (iy1) *iy1 = 0; 1583 | } else { 1584 | // move to integral bboxes (treating pixels as little squares, what pixels get touched)? 1585 | if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); 1586 | if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); 1587 | if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); 1588 | if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); 1589 | } 1590 | } 1591 | 1592 | 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) 1593 | { 1594 | stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); 1595 | } 1596 | 1597 | 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) 1598 | { 1599 | stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); 1600 | } 1601 | 1602 | 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) 1603 | { 1604 | stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); 1605 | } 1606 | 1607 | ////////////////////////////////////////////////////////////////////////////// 1608 | // 1609 | // Rasterizer 1610 | 1611 | typedef struct stbtt__hheap_chunk 1612 | { 1613 | struct stbtt__hheap_chunk *next; 1614 | } stbtt__hheap_chunk; 1615 | 1616 | typedef struct stbtt__hheap 1617 | { 1618 | struct stbtt__hheap_chunk *head; 1619 | void *first_free; 1620 | int num_remaining_in_head_chunk; 1621 | } stbtt__hheap; 1622 | 1623 | static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) 1624 | { 1625 | if (hh->first_free) { 1626 | void *p = hh->first_free; 1627 | hh->first_free = * (void **) p; 1628 | return p; 1629 | } else { 1630 | if (hh->num_remaining_in_head_chunk == 0) { 1631 | int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); 1632 | stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); 1633 | if (c == NULL) 1634 | return NULL; 1635 | c->next = hh->head; 1636 | hh->head = c; 1637 | hh->num_remaining_in_head_chunk = count; 1638 | } 1639 | --hh->num_remaining_in_head_chunk; 1640 | return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk; 1641 | } 1642 | } 1643 | 1644 | static void stbtt__hheap_free(stbtt__hheap *hh, void *p) 1645 | { 1646 | *(void **) p = hh->first_free; 1647 | hh->first_free = p; 1648 | } 1649 | 1650 | static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) 1651 | { 1652 | stbtt__hheap_chunk *c = hh->head; 1653 | while (c) { 1654 | stbtt__hheap_chunk *n = c->next; 1655 | STBTT_free(c, userdata); 1656 | c = n; 1657 | } 1658 | } 1659 | 1660 | typedef struct stbtt__edge { 1661 | float x0,y0, x1,y1; 1662 | int invert; 1663 | } stbtt__edge; 1664 | 1665 | 1666 | typedef struct stbtt__active_edge 1667 | { 1668 | struct stbtt__active_edge *next; 1669 | #if STBTT_RASTERIZER_VERSION==1 1670 | int x,dx; 1671 | float ey; 1672 | int direction; 1673 | #elif STBTT_RASTERIZER_VERSION==2 1674 | float fx,fdx,fdy; 1675 | float direction; 1676 | float sy; 1677 | float ey; 1678 | #else 1679 | #error "Unrecognized value of STBTT_RASTERIZER_VERSION" 1680 | #endif 1681 | } stbtt__active_edge; 1682 | 1683 | #if STBTT_RASTERIZER_VERSION == 1 1684 | #define STBTT_FIXSHIFT 10 1685 | #define STBTT_FIX (1 << STBTT_FIXSHIFT) 1686 | #define STBTT_FIXMASK (STBTT_FIX-1) 1687 | 1688 | static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) 1689 | { 1690 | stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); 1691 | float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); 1692 | STBTT_assert(z != NULL); 1693 | if (!z) return z; 1694 | 1695 | // round dx down to avoid overshooting 1696 | if (dxdy < 0) 1697 | z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); 1698 | else 1699 | z->dx = STBTT_ifloor(STBTT_FIX * dxdy); 1700 | 1701 | z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount 1702 | z->x -= off_x * STBTT_FIX; 1703 | 1704 | z->ey = e->y1; 1705 | z->next = 0; 1706 | z->direction = e->invert ? 1 : -1; 1707 | return z; 1708 | } 1709 | #elif STBTT_RASTERIZER_VERSION == 2 1710 | static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) 1711 | { 1712 | stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); 1713 | float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); 1714 | STBTT_assert(z != NULL); 1715 | //STBTT_assert(e->y0 <= start_point); 1716 | if (!z) return z; 1717 | z->fdx = dxdy; 1718 | z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; 1719 | z->fx = e->x0 + dxdy * (start_point - e->y0); 1720 | z->fx -= off_x; 1721 | z->direction = e->invert ? 1.0f : -1.0f; 1722 | z->sy = e->y0; 1723 | z->ey = e->y1; 1724 | z->next = 0; 1725 | return z; 1726 | } 1727 | #else 1728 | #error "Unrecognized value of STBTT_RASTERIZER_VERSION" 1729 | #endif 1730 | 1731 | #if STBTT_RASTERIZER_VERSION == 1 1732 | // note: this routine clips fills that extend off the edges... ideally this 1733 | // wouldn't happen, but it could happen if the truetype glyph bounding boxes 1734 | // are wrong, or if the user supplies a too-small bitmap 1735 | static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) 1736 | { 1737 | // non-zero winding fill 1738 | int x0=0, w=0; 1739 | 1740 | while (e) { 1741 | if (w == 0) { 1742 | // if we're currently at zero, we need to record the edge start point 1743 | x0 = e->x; w += e->direction; 1744 | } else { 1745 | int x1 = e->x; w += e->direction; 1746 | // if we went to zero, we need to draw 1747 | if (w == 0) { 1748 | int i = x0 >> STBTT_FIXSHIFT; 1749 | int j = x1 >> STBTT_FIXSHIFT; 1750 | 1751 | if (i < len && j >= 0) { 1752 | if (i == j) { 1753 | // x0,x1 are the same pixel, so compute combined coverage 1754 | scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); 1755 | } else { 1756 | if (i >= 0) // add antialiasing for x0 1757 | scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); 1758 | else 1759 | i = -1; // clip 1760 | 1761 | if (j < len) // add antialiasing for x1 1762 | scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); 1763 | else 1764 | j = len; // clip 1765 | 1766 | for (++i; i < j; ++i) // fill pixels between x0 and x1 1767 | scanline[i] = scanline[i] + (stbtt_uint8) max_weight; 1768 | } 1769 | } 1770 | } 1771 | } 1772 | 1773 | e = e->next; 1774 | } 1775 | } 1776 | 1777 | static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) 1778 | { 1779 | stbtt__hheap hh = { 0, 0, 0 }; 1780 | stbtt__active_edge *active = NULL; 1781 | int y,j=0; 1782 | int max_weight = (255 / vsubsample); // weight per vertical scanline 1783 | int s; // vertical subsample index 1784 | unsigned char scanline_data[512], *scanline; 1785 | 1786 | if (result->w > 512) 1787 | scanline = (unsigned char *) STBTT_malloc(result->w, userdata); 1788 | else 1789 | scanline = scanline_data; 1790 | 1791 | y = off_y * vsubsample; 1792 | e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; 1793 | 1794 | while (j < result->h) { 1795 | STBTT_memset(scanline, 0, result->w); 1796 | for (s=0; s < vsubsample; ++s) { 1797 | // find center of pixel for this scanline 1798 | float scan_y = y + 0.5f; 1799 | stbtt__active_edge **step = &active; 1800 | 1801 | // update all active edges; 1802 | // remove all active edges that terminate before the center of this scanline 1803 | while (*step) { 1804 | stbtt__active_edge * z = *step; 1805 | if (z->ey <= scan_y) { 1806 | *step = z->next; // delete from list 1807 | STBTT_assert(z->direction); 1808 | z->direction = 0; 1809 | stbtt__hheap_free(&hh, z); 1810 | } else { 1811 | z->x += z->dx; // advance to position for current scanline 1812 | step = &((*step)->next); // advance through list 1813 | } 1814 | } 1815 | 1816 | // resort the list if needed 1817 | for(;;) { 1818 | int changed=0; 1819 | step = &active; 1820 | while (*step && (*step)->next) { 1821 | if ((*step)->x > (*step)->next->x) { 1822 | stbtt__active_edge *t = *step; 1823 | stbtt__active_edge *q = t->next; 1824 | 1825 | t->next = q->next; 1826 | q->next = t; 1827 | *step = q; 1828 | changed = 1; 1829 | } 1830 | step = &(*step)->next; 1831 | } 1832 | if (!changed) break; 1833 | } 1834 | 1835 | // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline 1836 | while (e->y0 <= scan_y) { 1837 | if (e->y1 > scan_y) { 1838 | stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); 1839 | if (z != NULL) { 1840 | // find insertion point 1841 | if (active == NULL) 1842 | active = z; 1843 | else if (z->x < active->x) { 1844 | // insert at front 1845 | z->next = active; 1846 | active = z; 1847 | } else { 1848 | // find thing to insert AFTER 1849 | stbtt__active_edge *p = active; 1850 | while (p->next && p->next->x < z->x) 1851 | p = p->next; 1852 | // at this point, p->next->x is NOT < z->x 1853 | z->next = p->next; 1854 | p->next = z; 1855 | } 1856 | } 1857 | } 1858 | ++e; 1859 | } 1860 | 1861 | // now process all active edges in XOR fashion 1862 | if (active) 1863 | stbtt__fill_active_edges(scanline, result->w, active, max_weight); 1864 | 1865 | ++y; 1866 | } 1867 | STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); 1868 | ++j; 1869 | } 1870 | 1871 | stbtt__hheap_cleanup(&hh, userdata); 1872 | 1873 | if (scanline != scanline_data) 1874 | STBTT_free(scanline, userdata); 1875 | } 1876 | 1877 | #elif STBTT_RASTERIZER_VERSION == 2 1878 | 1879 | // the edge passed in here does not cross the vertical line at x or the vertical line at x+1 1880 | // (i.e. it has already been clipped to those) 1881 | static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) 1882 | { 1883 | if (y0 == y1) return; 1884 | STBTT_assert(y0 < y1); 1885 | STBTT_assert(e->sy <= e->ey); 1886 | if (y0 > e->ey) return; 1887 | if (y1 < e->sy) return; 1888 | if (y0 < e->sy) { 1889 | x0 += (x1-x0) * (e->sy - y0) / (y1-y0); 1890 | y0 = e->sy; 1891 | } 1892 | if (y1 > e->ey) { 1893 | x1 += (x1-x0) * (e->ey - y1) / (y1-y0); 1894 | y1 = e->ey; 1895 | } 1896 | 1897 | if (x0 == x) 1898 | STBTT_assert(x1 <= x+1); 1899 | else if (x0 == x+1) 1900 | STBTT_assert(x1 >= x); 1901 | else if (x0 <= x) 1902 | STBTT_assert(x1 <= x); 1903 | else if (x0 >= x+1) 1904 | STBTT_assert(x1 >= x+1); 1905 | else 1906 | STBTT_assert(x1 >= x && x1 <= x+1); 1907 | 1908 | if (x0 <= x && x1 <= x) 1909 | scanline[x] += e->direction * (y1-y0); 1910 | else if (x0 >= x+1 && x1 >= x+1) 1911 | ; 1912 | else { 1913 | STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); 1914 | scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position 1915 | } 1916 | } 1917 | 1918 | static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) 1919 | { 1920 | float y_bottom = y_top+1; 1921 | 1922 | while (e) { 1923 | // brute force every pixel 1924 | 1925 | // compute intersection points with top & bottom 1926 | STBTT_assert(e->ey >= y_top); 1927 | 1928 | if (e->fdx == 0) { 1929 | float x0 = e->fx; 1930 | if (x0 < len) { 1931 | if (x0 >= 0) { 1932 | stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); 1933 | stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); 1934 | } else { 1935 | stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); 1936 | } 1937 | } 1938 | } else { 1939 | float x0 = e->fx; 1940 | float dx = e->fdx; 1941 | float xb = x0 + dx; 1942 | float x_top, x_bottom; 1943 | float sy0,sy1; 1944 | float dy = e->fdy; 1945 | STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); 1946 | 1947 | // compute endpoints of line segment clipped to this scanline (if the 1948 | // line segment starts on this scanline. x0 is the intersection of the 1949 | // line with y_top, but that may be off the line segment. 1950 | if (e->sy > y_top) { 1951 | x_top = x0 + dx * (e->sy - y_top); 1952 | sy0 = e->sy; 1953 | } else { 1954 | x_top = x0; 1955 | sy0 = y_top; 1956 | } 1957 | if (e->ey < y_bottom) { 1958 | x_bottom = x0 + dx * (e->ey - y_top); 1959 | sy1 = e->ey; 1960 | } else { 1961 | x_bottom = xb; 1962 | sy1 = y_bottom; 1963 | } 1964 | 1965 | if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { 1966 | // from here on, we don't have to range check x values 1967 | 1968 | if ((int) x_top == (int) x_bottom) { 1969 | float height; 1970 | // simple case, only spans one pixel 1971 | int x = (int) x_top; 1972 | height = sy1 - sy0; 1973 | STBTT_assert(x >= 0 && x < len); 1974 | scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; 1975 | scanline_fill[x] += e->direction * height; // everything right of this pixel is filled 1976 | } else { 1977 | int x,x1,x2; 1978 | float y_crossing, step, sign, area; 1979 | // covers 2+ pixels 1980 | if (x_top > x_bottom) { 1981 | // flip scanline vertically; signed area is the same 1982 | float t; 1983 | sy0 = y_bottom - (sy0 - y_top); 1984 | sy1 = y_bottom - (sy1 - y_top); 1985 | t = sy0, sy0 = sy1, sy1 = t; 1986 | t = x_bottom, x_bottom = x_top, x_top = t; 1987 | dx = -dx; 1988 | dy = -dy; 1989 | t = x0, x0 = xb, xb = t; 1990 | } 1991 | 1992 | x1 = (int) x_top; 1993 | x2 = (int) x_bottom; 1994 | // compute intersection with y axis at x1+1 1995 | y_crossing = (x1+1 - x0) * dy + y_top; 1996 | 1997 | sign = e->direction; 1998 | // area of the rectangle covered from y0..y_crossing 1999 | area = sign * (y_crossing-sy0); 2000 | // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) 2001 | scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); 2002 | 2003 | step = sign * dy; 2004 | for (x = x1+1; x < x2; ++x) { 2005 | scanline[x] += area + step/2; 2006 | area += step; 2007 | } 2008 | y_crossing += dy * (x2 - (x1+1)); 2009 | 2010 | STBTT_assert(STBTT_fabs(area) <= 1.01f); 2011 | 2012 | scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); 2013 | 2014 | scanline_fill[x2] += sign * (sy1-sy0); 2015 | } 2016 | } else { 2017 | // if edge goes outside of box we're drawing, we require 2018 | // clipping logic. since this does not match the intended use 2019 | // of this library, we use a different, very slow brute 2020 | // force implementation 2021 | int x; 2022 | for (x=0; x < len; ++x) { 2023 | // cases: 2024 | // 2025 | // there can be up to two intersections with the pixel. any intersection 2026 | // with left or right edges can be handled by splitting into two (or three) 2027 | // regions. intersections with top & bottom do not necessitate case-wise logic. 2028 | // 2029 | // the old way of doing this found the intersections with the left & right edges, 2030 | // then used some simple logic to produce up to three segments in sorted order 2031 | // from top-to-bottom. however, this had a problem: if an x edge was epsilon 2032 | // across the x border, then the corresponding y position might not be distinct 2033 | // from the other y segment, and it might ignored as an empty segment. to avoid 2034 | // that, we need to explicitly produce segments based on x positions. 2035 | 2036 | // rename variables to clear pairs 2037 | float y0 = y_top; 2038 | float x1 = (float) (x); 2039 | float x2 = (float) (x+1); 2040 | float x3 = xb; 2041 | float y3 = y_bottom; 2042 | float y1,y2; 2043 | 2044 | // x = e->x + e->dx * (y-y_top) 2045 | // (y-y_top) = (x - e->x) / e->dx 2046 | // y = (x - e->x) / e->dx + y_top 2047 | y1 = (x - x0) / dx + y_top; 2048 | y2 = (x+1 - x0) / dx + y_top; 2049 | 2050 | if (x0 < x1 && x3 > x2) { // three segments descending down-right 2051 | stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); 2052 | stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); 2053 | stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); 2054 | } else if (x3 < x1 && x0 > x2) { // three segments descending down-left 2055 | stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); 2056 | stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); 2057 | stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); 2058 | } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right 2059 | stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); 2060 | stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); 2061 | } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left 2062 | stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); 2063 | stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); 2064 | } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right 2065 | stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); 2066 | stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); 2067 | } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left 2068 | stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); 2069 | stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); 2070 | } else { // one segment 2071 | stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); 2072 | } 2073 | } 2074 | } 2075 | } 2076 | e = e->next; 2077 | } 2078 | } 2079 | 2080 | // directly AA rasterize edges w/o supersampling 2081 | static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) 2082 | { 2083 | stbtt__hheap hh = { 0, 0, 0 }; 2084 | stbtt__active_edge *active = NULL; 2085 | int y,j=0, i; 2086 | float scanline_data[129], *scanline, *scanline2; 2087 | 2088 | STBTT__NOTUSED(vsubsample); 2089 | 2090 | if (result->w > 64) 2091 | scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); 2092 | else 2093 | scanline = scanline_data; 2094 | 2095 | scanline2 = scanline + result->w; 2096 | 2097 | y = off_y; 2098 | e[n].y0 = (float) (off_y + result->h) + 1; 2099 | 2100 | while (j < result->h) { 2101 | // find center of pixel for this scanline 2102 | float scan_y_top = y + 0.0f; 2103 | float scan_y_bottom = y + 1.0f; 2104 | stbtt__active_edge **step = &active; 2105 | 2106 | STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); 2107 | STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); 2108 | 2109 | // update all active edges; 2110 | // remove all active edges that terminate before the top of this scanline 2111 | while (*step) { 2112 | stbtt__active_edge * z = *step; 2113 | if (z->ey <= scan_y_top) { 2114 | *step = z->next; // delete from list 2115 | STBTT_assert(z->direction); 2116 | z->direction = 0; 2117 | stbtt__hheap_free(&hh, z); 2118 | } else { 2119 | step = &((*step)->next); // advance through list 2120 | } 2121 | } 2122 | 2123 | // insert all edges that start before the bottom of this scanline 2124 | while (e->y0 <= scan_y_bottom) { 2125 | if (e->y0 != e->y1) { 2126 | stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); 2127 | if (z != NULL) { 2128 | STBTT_assert(z->ey >= scan_y_top); 2129 | // insert at front 2130 | z->next = active; 2131 | active = z; 2132 | } 2133 | } 2134 | ++e; 2135 | } 2136 | 2137 | // now process all active edges 2138 | if (active) 2139 | stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); 2140 | 2141 | { 2142 | float sum = 0; 2143 | for (i=0; i < result->w; ++i) { 2144 | float k; 2145 | int m; 2146 | sum += scanline2[i]; 2147 | k = scanline[i] + sum; 2148 | k = (float) STBTT_fabs(k)*255 + 0.5f; 2149 | m = (int) k; 2150 | if (m > 255) m = 255; 2151 | result->pixels[j*result->stride + i] = (unsigned char) m; 2152 | } 2153 | } 2154 | // advance all the edges 2155 | step = &active; 2156 | while (*step) { 2157 | stbtt__active_edge *z = *step; 2158 | z->fx += z->fdx; // advance to position for current scanline 2159 | step = &((*step)->next); // advance through list 2160 | } 2161 | 2162 | ++y; 2163 | ++j; 2164 | } 2165 | 2166 | stbtt__hheap_cleanup(&hh, userdata); 2167 | 2168 | if (scanline != scanline_data) 2169 | STBTT_free(scanline, userdata); 2170 | } 2171 | #else 2172 | #error "Unrecognized value of STBTT_RASTERIZER_VERSION" 2173 | #endif 2174 | 2175 | #define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) 2176 | 2177 | static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) 2178 | { 2179 | int i,j; 2180 | for (i=1; i < n; ++i) { 2181 | stbtt__edge t = p[i], *a = &t; 2182 | j = i; 2183 | while (j > 0) { 2184 | stbtt__edge *b = &p[j-1]; 2185 | int c = STBTT__COMPARE(a,b); 2186 | if (!c) break; 2187 | p[j] = p[j-1]; 2188 | --j; 2189 | } 2190 | if (i != j) 2191 | p[j] = t; 2192 | } 2193 | } 2194 | 2195 | static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) 2196 | { 2197 | /* threshhold for transitioning to insertion sort */ 2198 | while (n > 12) { 2199 | stbtt__edge t; 2200 | int c01,c12,c,m,i,j; 2201 | 2202 | /* compute median of three */ 2203 | m = n >> 1; 2204 | c01 = STBTT__COMPARE(&p[0],&p[m]); 2205 | c12 = STBTT__COMPARE(&p[m],&p[n-1]); 2206 | /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ 2207 | if (c01 != c12) { 2208 | /* otherwise, we'll need to swap something else to middle */ 2209 | int z; 2210 | c = STBTT__COMPARE(&p[0],&p[n-1]); 2211 | /* 0>mid && midn => n; 0 0 */ 2212 | /* 0n: 0>n => 0; 0 n */ 2213 | z = (c == c12) ? 0 : n-1; 2214 | t = p[z]; 2215 | p[z] = p[m]; 2216 | p[m] = t; 2217 | } 2218 | /* now p[m] is the median-of-three */ 2219 | /* swap it to the beginning so it won't move around */ 2220 | t = p[0]; 2221 | p[0] = p[m]; 2222 | p[m] = t; 2223 | 2224 | /* partition loop */ 2225 | i=1; 2226 | j=n-1; 2227 | for(;;) { 2228 | /* handling of equality is crucial here */ 2229 | /* for sentinels & efficiency with duplicates */ 2230 | for (;;++i) { 2231 | if (!STBTT__COMPARE(&p[i], &p[0])) break; 2232 | } 2233 | for (;;--j) { 2234 | if (!STBTT__COMPARE(&p[0], &p[j])) break; 2235 | } 2236 | /* make sure we haven't crossed */ 2237 | if (i >= j) break; 2238 | t = p[i]; 2239 | p[i] = p[j]; 2240 | p[j] = t; 2241 | 2242 | ++i; 2243 | --j; 2244 | } 2245 | /* recurse on smaller side, iterate on larger */ 2246 | if (j < (n-i)) { 2247 | stbtt__sort_edges_quicksort(p,j); 2248 | p = p+i; 2249 | n = n-i; 2250 | } else { 2251 | stbtt__sort_edges_quicksort(p+i, n-i); 2252 | n = j; 2253 | } 2254 | } 2255 | } 2256 | 2257 | static void stbtt__sort_edges(stbtt__edge *p, int n) 2258 | { 2259 | stbtt__sort_edges_quicksort(p, n); 2260 | stbtt__sort_edges_ins_sort(p, n); 2261 | } 2262 | 2263 | typedef struct 2264 | { 2265 | float x,y; 2266 | } stbtt__point; 2267 | 2268 | 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) 2269 | { 2270 | float y_scale_inv = invert ? -scale_y : scale_y; 2271 | stbtt__edge *e; 2272 | int n,i,j,k,m; 2273 | #if STBTT_RASTERIZER_VERSION == 1 2274 | int vsubsample = result->h < 8 ? 15 : 5; 2275 | #elif STBTT_RASTERIZER_VERSION == 2 2276 | int vsubsample = 1; 2277 | #else 2278 | #error "Unrecognized value of STBTT_RASTERIZER_VERSION" 2279 | #endif 2280 | // vsubsample should divide 255 evenly; otherwise we won't reach full opacity 2281 | 2282 | // now we have to blow out the windings into explicit edge lists 2283 | n = 0; 2284 | for (i=0; i < windings; ++i) 2285 | n += wcount[i]; 2286 | 2287 | e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel 2288 | if (e == 0) return; 2289 | n = 0; 2290 | 2291 | m=0; 2292 | for (i=0; i < windings; ++i) { 2293 | stbtt__point *p = pts + m; 2294 | m += wcount[i]; 2295 | j = wcount[i]-1; 2296 | for (k=0; k < wcount[i]; j=k++) { 2297 | int a=k,b=j; 2298 | // skip the edge if horizontal 2299 | if (p[j].y == p[k].y) 2300 | continue; 2301 | // add edge from j to k to the list 2302 | e[n].invert = 0; 2303 | if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { 2304 | e[n].invert = 1; 2305 | a=j,b=k; 2306 | } 2307 | e[n].x0 = p[a].x * scale_x + shift_x; 2308 | e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; 2309 | e[n].x1 = p[b].x * scale_x + shift_x; 2310 | e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; 2311 | ++n; 2312 | } 2313 | } 2314 | 2315 | // now sort the edges by their highest point (should snap to integer, and then by x) 2316 | //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); 2317 | stbtt__sort_edges(e, n); 2318 | 2319 | // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule 2320 | stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); 2321 | 2322 | STBTT_free(e, userdata); 2323 | } 2324 | 2325 | static void stbtt__add_point(stbtt__point *points, int n, float x, float y) 2326 | { 2327 | if (!points) return; // during first pass, it's unallocated 2328 | points[n].x = x; 2329 | points[n].y = y; 2330 | } 2331 | 2332 | // tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching 2333 | 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) 2334 | { 2335 | // midpoint 2336 | float mx = (x0 + 2*x1 + x2)/4; 2337 | float my = (y0 + 2*y1 + y2)/4; 2338 | // versus directly drawn line 2339 | float dx = (x0+x2)/2 - mx; 2340 | float dy = (y0+y2)/2 - my; 2341 | if (n > 16) // 65536 segments on one curve better be enough! 2342 | return 1; 2343 | if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA 2344 | stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); 2345 | stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); 2346 | } else { 2347 | stbtt__add_point(points, *num_points,x2,y2); 2348 | *num_points = *num_points+1; 2349 | } 2350 | return 1; 2351 | } 2352 | 2353 | // returns number of contours 2354 | static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) 2355 | { 2356 | stbtt__point *points=0; 2357 | int num_points=0; 2358 | 2359 | float objspace_flatness_squared = objspace_flatness * objspace_flatness; 2360 | int i,n=0,start=0, pass; 2361 | 2362 | // count how many "moves" there are to get the contour count 2363 | for (i=0; i < num_verts; ++i) 2364 | if (vertices[i].type == STBTT_vmove) 2365 | ++n; 2366 | 2367 | *num_contours = n; 2368 | if (n == 0) return 0; 2369 | 2370 | *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); 2371 | 2372 | if (*contour_lengths == 0) { 2373 | *num_contours = 0; 2374 | return 0; 2375 | } 2376 | 2377 | // make two passes through the points so we don't need to realloc 2378 | for (pass=0; pass < 2; ++pass) { 2379 | float x=0,y=0; 2380 | if (pass == 1) { 2381 | points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); 2382 | if (points == NULL) goto error; 2383 | } 2384 | num_points = 0; 2385 | n= -1; 2386 | for (i=0; i < num_verts; ++i) { 2387 | switch (vertices[i].type) { 2388 | case STBTT_vmove: 2389 | // start the next contour 2390 | if (n >= 0) 2391 | (*contour_lengths)[n] = num_points - start; 2392 | ++n; 2393 | start = num_points; 2394 | 2395 | x = vertices[i].x, y = vertices[i].y; 2396 | stbtt__add_point(points, num_points++, x,y); 2397 | break; 2398 | case STBTT_vline: 2399 | x = vertices[i].x, y = vertices[i].y; 2400 | stbtt__add_point(points, num_points++, x, y); 2401 | break; 2402 | case STBTT_vcurve: 2403 | stbtt__tesselate_curve(points, &num_points, x,y, 2404 | vertices[i].cx, vertices[i].cy, 2405 | vertices[i].x, vertices[i].y, 2406 | objspace_flatness_squared, 0); 2407 | x = vertices[i].x, y = vertices[i].y; 2408 | break; 2409 | } 2410 | } 2411 | (*contour_lengths)[n] = num_points - start; 2412 | } 2413 | 2414 | return points; 2415 | error: 2416 | STBTT_free(points, userdata); 2417 | STBTT_free(*contour_lengths, userdata); 2418 | *contour_lengths = 0; 2419 | *num_contours = 0; 2420 | return NULL; 2421 | } 2422 | 2423 | 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) 2424 | { 2425 | float scale = scale_x > scale_y ? scale_y : scale_x; 2426 | int winding_count, *winding_lengths; 2427 | stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); 2428 | if (windings) { 2429 | stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); 2430 | STBTT_free(winding_lengths, userdata); 2431 | STBTT_free(windings, userdata); 2432 | } 2433 | } 2434 | 2435 | STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) 2436 | { 2437 | STBTT_free(bitmap, userdata); 2438 | } 2439 | 2440 | 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) 2441 | { 2442 | int ix0,iy0,ix1,iy1; 2443 | stbtt__bitmap gbm; 2444 | stbtt_vertex *vertices; 2445 | int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 2446 | 2447 | if (scale_x == 0) scale_x = scale_y; 2448 | if (scale_y == 0) { 2449 | if (scale_x == 0) { 2450 | STBTT_free(vertices, info->userdata); 2451 | return NULL; 2452 | } 2453 | scale_y = scale_x; 2454 | } 2455 | 2456 | stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); 2457 | 2458 | // now we get the size 2459 | gbm.w = (ix1 - ix0); 2460 | gbm.h = (iy1 - iy0); 2461 | gbm.pixels = NULL; // in case we error 2462 | 2463 | if (width ) *width = gbm.w; 2464 | if (height) *height = gbm.h; 2465 | if (xoff ) *xoff = ix0; 2466 | if (yoff ) *yoff = iy0; 2467 | 2468 | if (gbm.w && gbm.h) { 2469 | gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); 2470 | if (gbm.pixels) { 2471 | gbm.stride = gbm.w; 2472 | 2473 | stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); 2474 | } 2475 | } 2476 | STBTT_free(vertices, info->userdata); 2477 | return gbm.pixels; 2478 | } 2479 | 2480 | 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) 2481 | { 2482 | return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); 2483 | } 2484 | 2485 | 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) 2486 | { 2487 | int ix0,iy0; 2488 | stbtt_vertex *vertices; 2489 | int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 2490 | stbtt__bitmap gbm; 2491 | 2492 | stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); 2493 | gbm.pixels = output; 2494 | gbm.w = out_w; 2495 | gbm.h = out_h; 2496 | gbm.stride = out_stride; 2497 | 2498 | if (gbm.w && gbm.h) 2499 | stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); 2500 | 2501 | STBTT_free(vertices, info->userdata); 2502 | } 2503 | 2504 | 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) 2505 | { 2506 | stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); 2507 | } 2508 | 2509 | 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) 2510 | { 2511 | return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); 2512 | } 2513 | 2514 | 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) 2515 | { 2516 | stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); 2517 | } 2518 | 2519 | 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) 2520 | { 2521 | return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); 2522 | } 2523 | 2524 | 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) 2525 | { 2526 | stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); 2527 | } 2528 | 2529 | ////////////////////////////////////////////////////////////////////////////// 2530 | // 2531 | // bitmap baking 2532 | // 2533 | // This is SUPER-CRAPPY packing to keep source code small 2534 | 2535 | STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) 2536 | float pixel_height, // height of font in pixels 2537 | unsigned char *pixels, int pw, int ph, // bitmap to be filled in 2538 | int first_char, int num_chars, // characters to bake 2539 | stbtt_bakedchar *chardata) 2540 | { 2541 | float scale; 2542 | int x,y,bottom_y, i; 2543 | stbtt_fontinfo f; 2544 | f.userdata = NULL; 2545 | if (!stbtt_InitFont(&f, data, offset)) 2546 | return -1; 2547 | STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels 2548 | x=y=1; 2549 | bottom_y = 1; 2550 | 2551 | scale = stbtt_ScaleForPixelHeight(&f, pixel_height); 2552 | 2553 | for (i=0; i < num_chars; ++i) { 2554 | int advance, lsb, x0,y0,x1,y1,gw,gh; 2555 | int g = stbtt_FindGlyphIndex(&f, first_char + i); 2556 | stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); 2557 | stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); 2558 | gw = x1-x0; 2559 | gh = y1-y0; 2560 | if (x + gw + 1 >= pw) 2561 | y = bottom_y, x = 1; // advance to next row 2562 | if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row 2563 | return -i; 2564 | STBTT_assert(x+gw < pw); 2565 | STBTT_assert(y+gh < ph); 2566 | stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); 2567 | chardata[i].x0 = (stbtt_int16) x; 2568 | chardata[i].y0 = (stbtt_int16) y; 2569 | chardata[i].x1 = (stbtt_int16) (x + gw); 2570 | chardata[i].y1 = (stbtt_int16) (y + gh); 2571 | chardata[i].xadvance = scale * advance; 2572 | chardata[i].xoff = (float) x0; 2573 | chardata[i].yoff = (float) y0; 2574 | x = x + gw + 1; 2575 | if (y+gh+1 > bottom_y) 2576 | bottom_y = y+gh+1; 2577 | } 2578 | return bottom_y; 2579 | } 2580 | 2581 | 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) 2582 | { 2583 | float d3d_bias = opengl_fillrule ? 0 : -0.5f; 2584 | float ipw = 1.0f / pw, iph = 1.0f / ph; 2585 | stbtt_bakedchar *b = chardata + char_index; 2586 | int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); 2587 | int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); 2588 | 2589 | q->x0 = round_x + d3d_bias; 2590 | q->y0 = round_y + d3d_bias; 2591 | q->x1 = round_x + b->x1 - b->x0 + d3d_bias; 2592 | q->y1 = round_y + b->y1 - b->y0 + d3d_bias; 2593 | 2594 | q->s0 = b->x0 * ipw; 2595 | q->t0 = b->y0 * iph; 2596 | q->s1 = b->x1 * ipw; 2597 | q->t1 = b->y1 * iph; 2598 | 2599 | *xpos += b->xadvance; 2600 | } 2601 | 2602 | ////////////////////////////////////////////////////////////////////////////// 2603 | // 2604 | // rectangle packing replacement routines if you don't have stb_rect_pack.h 2605 | // 2606 | 2607 | #ifndef STB_RECT_PACK_VERSION 2608 | 2609 | typedef int stbrp_coord; 2610 | 2611 | //////////////////////////////////////////////////////////////////////////////////// 2612 | // // 2613 | // // 2614 | // COMPILER WARNING ?!?!? // 2615 | // // 2616 | // // 2617 | // if you get a compile warning due to these symbols being defined more than // 2618 | // once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // 2619 | // // 2620 | //////////////////////////////////////////////////////////////////////////////////// 2621 | 2622 | typedef struct 2623 | { 2624 | int width,height; 2625 | int x,y,bottom_y; 2626 | } stbrp_context; 2627 | 2628 | typedef struct 2629 | { 2630 | unsigned char x; 2631 | } stbrp_node; 2632 | 2633 | struct stbrp_rect 2634 | { 2635 | stbrp_coord x,y; 2636 | int id,w,h,was_packed; 2637 | }; 2638 | 2639 | static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) 2640 | { 2641 | con->width = pw; 2642 | con->height = ph; 2643 | con->x = 0; 2644 | con->y = 0; 2645 | con->bottom_y = 0; 2646 | STBTT__NOTUSED(nodes); 2647 | STBTT__NOTUSED(num_nodes); 2648 | } 2649 | 2650 | static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) 2651 | { 2652 | int i; 2653 | for (i=0; i < num_rects; ++i) { 2654 | if (con->x + rects[i].w > con->width) { 2655 | con->x = 0; 2656 | con->y = con->bottom_y; 2657 | } 2658 | if (con->y + rects[i].h > con->height) 2659 | break; 2660 | rects[i].x = con->x; 2661 | rects[i].y = con->y; 2662 | rects[i].was_packed = 1; 2663 | con->x += rects[i].w; 2664 | if (con->y + rects[i].h > con->bottom_y) 2665 | con->bottom_y = con->y + rects[i].h; 2666 | } 2667 | for ( ; i < num_rects; ++i) 2668 | rects[i].was_packed = 0; 2669 | } 2670 | #endif 2671 | 2672 | ////////////////////////////////////////////////////////////////////////////// 2673 | // 2674 | // bitmap baking 2675 | // 2676 | // This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If 2677 | // stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. 2678 | 2679 | 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) 2680 | { 2681 | stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); 2682 | int num_nodes = pw - padding; 2683 | stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); 2684 | 2685 | if (context == NULL || nodes == NULL) { 2686 | if (context != NULL) STBTT_free(context, alloc_context); 2687 | if (nodes != NULL) STBTT_free(nodes , alloc_context); 2688 | return 0; 2689 | } 2690 | 2691 | spc->user_allocator_context = alloc_context; 2692 | spc->width = pw; 2693 | spc->height = ph; 2694 | spc->pixels = pixels; 2695 | spc->pack_info = context; 2696 | spc->nodes = nodes; 2697 | spc->padding = padding; 2698 | spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; 2699 | spc->h_oversample = 1; 2700 | spc->v_oversample = 1; 2701 | 2702 | stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); 2703 | 2704 | if (pixels) 2705 | STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels 2706 | 2707 | return 1; 2708 | } 2709 | 2710 | STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) 2711 | { 2712 | STBTT_free(spc->nodes , spc->user_allocator_context); 2713 | STBTT_free(spc->pack_info, spc->user_allocator_context); 2714 | } 2715 | 2716 | STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) 2717 | { 2718 | STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); 2719 | STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); 2720 | if (h_oversample <= STBTT_MAX_OVERSAMPLE) 2721 | spc->h_oversample = h_oversample; 2722 | if (v_oversample <= STBTT_MAX_OVERSAMPLE) 2723 | spc->v_oversample = v_oversample; 2724 | } 2725 | 2726 | #define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) 2727 | 2728 | static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) 2729 | { 2730 | unsigned char buffer[STBTT_MAX_OVERSAMPLE]; 2731 | int safe_w = w - kernel_width; 2732 | int j; 2733 | STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze 2734 | for (j=0; j < h; ++j) { 2735 | int i; 2736 | unsigned int total; 2737 | STBTT_memset(buffer, 0, kernel_width); 2738 | 2739 | total = 0; 2740 | 2741 | // make kernel_width a constant in common cases so compiler can optimize out the divide 2742 | switch (kernel_width) { 2743 | case 2: 2744 | for (i=0; i <= safe_w; ++i) { 2745 | total += pixels[i] - buffer[i & STBTT__OVER_MASK]; 2746 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; 2747 | pixels[i] = (unsigned char) (total / 2); 2748 | } 2749 | break; 2750 | case 3: 2751 | for (i=0; i <= safe_w; ++i) { 2752 | total += pixels[i] - buffer[i & STBTT__OVER_MASK]; 2753 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; 2754 | pixels[i] = (unsigned char) (total / 3); 2755 | } 2756 | break; 2757 | case 4: 2758 | for (i=0; i <= safe_w; ++i) { 2759 | total += pixels[i] - buffer[i & STBTT__OVER_MASK]; 2760 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; 2761 | pixels[i] = (unsigned char) (total / 4); 2762 | } 2763 | break; 2764 | case 5: 2765 | for (i=0; i <= safe_w; ++i) { 2766 | total += pixels[i] - buffer[i & STBTT__OVER_MASK]; 2767 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; 2768 | pixels[i] = (unsigned char) (total / 5); 2769 | } 2770 | break; 2771 | default: 2772 | for (i=0; i <= safe_w; ++i) { 2773 | total += pixels[i] - buffer[i & STBTT__OVER_MASK]; 2774 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; 2775 | pixels[i] = (unsigned char) (total / kernel_width); 2776 | } 2777 | break; 2778 | } 2779 | 2780 | for (; i < w; ++i) { 2781 | STBTT_assert(pixels[i] == 0); 2782 | total -= buffer[i & STBTT__OVER_MASK]; 2783 | pixels[i] = (unsigned char) (total / kernel_width); 2784 | } 2785 | 2786 | pixels += stride_in_bytes; 2787 | } 2788 | } 2789 | 2790 | static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) 2791 | { 2792 | unsigned char buffer[STBTT_MAX_OVERSAMPLE]; 2793 | int safe_h = h - kernel_width; 2794 | int j; 2795 | STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze 2796 | for (j=0; j < w; ++j) { 2797 | int i; 2798 | unsigned int total; 2799 | STBTT_memset(buffer, 0, kernel_width); 2800 | 2801 | total = 0; 2802 | 2803 | // make kernel_width a constant in common cases so compiler can optimize out the divide 2804 | switch (kernel_width) { 2805 | case 2: 2806 | for (i=0; i <= safe_h; ++i) { 2807 | total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; 2808 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; 2809 | pixels[i*stride_in_bytes] = (unsigned char) (total / 2); 2810 | } 2811 | break; 2812 | case 3: 2813 | for (i=0; i <= safe_h; ++i) { 2814 | total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; 2815 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; 2816 | pixels[i*stride_in_bytes] = (unsigned char) (total / 3); 2817 | } 2818 | break; 2819 | case 4: 2820 | for (i=0; i <= safe_h; ++i) { 2821 | total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; 2822 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; 2823 | pixels[i*stride_in_bytes] = (unsigned char) (total / 4); 2824 | } 2825 | break; 2826 | case 5: 2827 | for (i=0; i <= safe_h; ++i) { 2828 | total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; 2829 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; 2830 | pixels[i*stride_in_bytes] = (unsigned char) (total / 5); 2831 | } 2832 | break; 2833 | default: 2834 | for (i=0; i <= safe_h; ++i) { 2835 | total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; 2836 | buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; 2837 | pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); 2838 | } 2839 | break; 2840 | } 2841 | 2842 | for (; i < h; ++i) { 2843 | STBTT_assert(pixels[i*stride_in_bytes] == 0); 2844 | total -= buffer[i & STBTT__OVER_MASK]; 2845 | pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); 2846 | } 2847 | 2848 | pixels += 1; 2849 | } 2850 | } 2851 | 2852 | static float stbtt__oversample_shift(int oversample) 2853 | { 2854 | if (!oversample) 2855 | return 0.0f; 2856 | 2857 | // The prefilter is a box filter of width "oversample", 2858 | // which shifts phase by (oversample - 1)/2 pixels in 2859 | // oversampled space. We want to shift in the opposite 2860 | // direction to counter this. 2861 | return (float)-(oversample - 1) / (2.0f * (float)oversample); 2862 | } 2863 | 2864 | // rects array must be big enough to accommodate all characters in the given ranges 2865 | STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) 2866 | { 2867 | int i,j,k; 2868 | 2869 | k=0; 2870 | for (i=0; i < num_ranges; ++i) { 2871 | float fh = ranges[i].font_size; 2872 | float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); 2873 | ranges[i].h_oversample = (unsigned char) spc->h_oversample; 2874 | ranges[i].v_oversample = (unsigned char) spc->v_oversample; 2875 | for (j=0; j < ranges[i].num_chars; ++j) { 2876 | int x0,y0,x1,y1; 2877 | int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; 2878 | int glyph = stbtt_FindGlyphIndex(info, codepoint); 2879 | stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, 2880 | scale * spc->h_oversample, 2881 | scale * spc->v_oversample, 2882 | 0,0, 2883 | &x0,&y0,&x1,&y1); 2884 | rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); 2885 | rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); 2886 | ++k; 2887 | } 2888 | } 2889 | 2890 | return k; 2891 | } 2892 | 2893 | // rects array must be big enough to accommodate all characters in the given ranges 2894 | STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) 2895 | { 2896 | int i,j,k, return_value = 1; 2897 | 2898 | // save current values 2899 | int old_h_over = spc->h_oversample; 2900 | int old_v_over = spc->v_oversample; 2901 | 2902 | k = 0; 2903 | for (i=0; i < num_ranges; ++i) { 2904 | float fh = ranges[i].font_size; 2905 | float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); 2906 | float recip_h,recip_v,sub_x,sub_y; 2907 | spc->h_oversample = ranges[i].h_oversample; 2908 | spc->v_oversample = ranges[i].v_oversample; 2909 | recip_h = 1.0f / spc->h_oversample; 2910 | recip_v = 1.0f / spc->v_oversample; 2911 | sub_x = stbtt__oversample_shift(spc->h_oversample); 2912 | sub_y = stbtt__oversample_shift(spc->v_oversample); 2913 | for (j=0; j < ranges[i].num_chars; ++j) { 2914 | stbrp_rect *r = &rects[k]; 2915 | if (r->was_packed) { 2916 | stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; 2917 | int advance, lsb, x0,y0,x1,y1; 2918 | int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; 2919 | int glyph = stbtt_FindGlyphIndex(info, codepoint); 2920 | stbrp_coord pad = (stbrp_coord) spc->padding; 2921 | 2922 | // pad on left and top 2923 | r->x += pad; 2924 | r->y += pad; 2925 | r->w -= pad; 2926 | r->h -= pad; 2927 | stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); 2928 | stbtt_GetGlyphBitmapBox(info, glyph, 2929 | scale * spc->h_oversample, 2930 | scale * spc->v_oversample, 2931 | &x0,&y0,&x1,&y1); 2932 | stbtt_MakeGlyphBitmapSubpixel(info, 2933 | spc->pixels + r->x + r->y*spc->stride_in_bytes, 2934 | r->w - spc->h_oversample+1, 2935 | r->h - spc->v_oversample+1, 2936 | spc->stride_in_bytes, 2937 | scale * spc->h_oversample, 2938 | scale * spc->v_oversample, 2939 | 0,0, 2940 | glyph); 2941 | 2942 | if (spc->h_oversample > 1) 2943 | stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, 2944 | r->w, r->h, spc->stride_in_bytes, 2945 | spc->h_oversample); 2946 | 2947 | if (spc->v_oversample > 1) 2948 | stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, 2949 | r->w, r->h, spc->stride_in_bytes, 2950 | spc->v_oversample); 2951 | 2952 | bc->x0 = (stbtt_int16) r->x; 2953 | bc->y0 = (stbtt_int16) r->y; 2954 | bc->x1 = (stbtt_int16) (r->x + r->w); 2955 | bc->y1 = (stbtt_int16) (r->y + r->h); 2956 | bc->xadvance = scale * advance; 2957 | bc->xoff = (float) x0 * recip_h + sub_x; 2958 | bc->yoff = (float) y0 * recip_v + sub_y; 2959 | bc->xoff2 = (x0 + r->w) * recip_h + sub_x; 2960 | bc->yoff2 = (y0 + r->h) * recip_v + sub_y; 2961 | } else { 2962 | return_value = 0; // if any fail, report failure 2963 | } 2964 | 2965 | ++k; 2966 | } 2967 | } 2968 | 2969 | // restore original values 2970 | spc->h_oversample = old_h_over; 2971 | spc->v_oversample = old_v_over; 2972 | 2973 | return return_value; 2974 | } 2975 | 2976 | STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) 2977 | { 2978 | stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); 2979 | } 2980 | 2981 | STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) 2982 | { 2983 | stbtt_fontinfo info; 2984 | int i,j,n, return_value = 1; 2985 | //stbrp_context *context = (stbrp_context *) spc->pack_info; 2986 | stbrp_rect *rects; 2987 | 2988 | // flag all characters as NOT packed 2989 | for (i=0; i < num_ranges; ++i) 2990 | for (j=0; j < ranges[i].num_chars; ++j) 2991 | ranges[i].chardata_for_range[j].x0 = 2992 | ranges[i].chardata_for_range[j].y0 = 2993 | ranges[i].chardata_for_range[j].x1 = 2994 | ranges[i].chardata_for_range[j].y1 = 0; 2995 | 2996 | n = 0; 2997 | for (i=0; i < num_ranges; ++i) 2998 | n += ranges[i].num_chars; 2999 | 3000 | rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); 3001 | if (rects == NULL) 3002 | return 0; 3003 | 3004 | info.userdata = spc->user_allocator_context; 3005 | stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); 3006 | 3007 | n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); 3008 | 3009 | stbtt_PackFontRangesPackRects(spc, rects, n); 3010 | 3011 | return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); 3012 | 3013 | STBTT_free(rects, spc->user_allocator_context); 3014 | return return_value; 3015 | } 3016 | 3017 | STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, 3018 | int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) 3019 | { 3020 | stbtt_pack_range range; 3021 | range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; 3022 | range.array_of_unicode_codepoints = NULL; 3023 | range.num_chars = num_chars_in_range; 3024 | range.chardata_for_range = chardata_for_range; 3025 | range.font_size = font_size; 3026 | return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); 3027 | } 3028 | 3029 | 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) 3030 | { 3031 | float ipw = 1.0f / pw, iph = 1.0f / ph; 3032 | stbtt_packedchar *b = chardata + char_index; 3033 | 3034 | if (align_to_integer) { 3035 | float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); 3036 | float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); 3037 | q->x0 = x; 3038 | q->y0 = y; 3039 | q->x1 = x + b->xoff2 - b->xoff; 3040 | q->y1 = y + b->yoff2 - b->yoff; 3041 | } else { 3042 | q->x0 = *xpos + b->xoff; 3043 | q->y0 = *ypos + b->yoff; 3044 | q->x1 = *xpos + b->xoff2; 3045 | q->y1 = *ypos + b->yoff2; 3046 | } 3047 | 3048 | q->s0 = b->x0 * ipw; 3049 | q->t0 = b->y0 * iph; 3050 | q->s1 = b->x1 * ipw; 3051 | q->t1 = b->y1 * iph; 3052 | 3053 | *xpos += b->xadvance; 3054 | } 3055 | 3056 | 3057 | ////////////////////////////////////////////////////////////////////////////// 3058 | // 3059 | // font name matching -- recommended not to use this 3060 | // 3061 | 3062 | // check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string 3063 | static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) 3064 | { 3065 | stbtt_int32 i=0; 3066 | 3067 | // convert utf16 to utf8 and compare the results while converting 3068 | while (len2) { 3069 | stbtt_uint16 ch = s2[0]*256 + s2[1]; 3070 | if (ch < 0x80) { 3071 | if (i >= len1) return -1; 3072 | if (s1[i++] != ch) return -1; 3073 | } else if (ch < 0x800) { 3074 | if (i+1 >= len1) return -1; 3075 | if (s1[i++] != 0xc0 + (ch >> 6)) return -1; 3076 | if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; 3077 | } else if (ch >= 0xd800 && ch < 0xdc00) { 3078 | stbtt_uint32 c; 3079 | stbtt_uint16 ch2 = s2[2]*256 + s2[3]; 3080 | if (i+3 >= len1) return -1; 3081 | c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; 3082 | if (s1[i++] != 0xf0 + (c >> 18)) return -1; 3083 | if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; 3084 | if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; 3085 | if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; 3086 | s2 += 2; // plus another 2 below 3087 | len2 -= 2; 3088 | } else if (ch >= 0xdc00 && ch < 0xe000) { 3089 | return -1; 3090 | } else { 3091 | if (i+2 >= len1) return -1; 3092 | if (s1[i++] != 0xe0 + (ch >> 12)) return -1; 3093 | if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; 3094 | if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; 3095 | } 3096 | s2 += 2; 3097 | len2 -= 2; 3098 | } 3099 | return i; 3100 | } 3101 | 3102 | STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) 3103 | { 3104 | return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); 3105 | } 3106 | 3107 | // returns results in whatever encoding you request... but note that 2-byte encodings 3108 | // will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare 3109 | STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) 3110 | { 3111 | stbtt_int32 i,count,stringOffset; 3112 | stbtt_uint8 *fc = font->data; 3113 | stbtt_uint32 offset = font->fontstart; 3114 | stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); 3115 | if (!nm) return NULL; 3116 | 3117 | count = ttUSHORT(fc+nm+2); 3118 | stringOffset = nm + ttUSHORT(fc+nm+4); 3119 | for (i=0; i < count; ++i) { 3120 | stbtt_uint32 loc = nm + 6 + 12 * i; 3121 | if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) 3122 | && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { 3123 | *length = ttUSHORT(fc+loc+8); 3124 | return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); 3125 | } 3126 | } 3127 | return NULL; 3128 | } 3129 | 3130 | static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) 3131 | { 3132 | stbtt_int32 i; 3133 | stbtt_int32 count = ttUSHORT(fc+nm+2); 3134 | stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); 3135 | 3136 | for (i=0; i < count; ++i) { 3137 | stbtt_uint32 loc = nm + 6 + 12 * i; 3138 | stbtt_int32 id = ttUSHORT(fc+loc+6); 3139 | if (id == target_id) { 3140 | // find the encoding 3141 | stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); 3142 | 3143 | // is this a Unicode encoding? 3144 | if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { 3145 | stbtt_int32 slen = ttUSHORT(fc+loc+8); 3146 | stbtt_int32 off = ttUSHORT(fc+loc+10); 3147 | 3148 | // check if there's a prefix match 3149 | stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); 3150 | if (matchlen >= 0) { 3151 | // check for target_id+1 immediately following, with same encoding & language 3152 | 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) { 3153 | slen = ttUSHORT(fc+loc+12+8); 3154 | off = ttUSHORT(fc+loc+12+10); 3155 | if (slen == 0) { 3156 | if (matchlen == nlen) 3157 | return 1; 3158 | } else if (matchlen < nlen && name[matchlen] == ' ') { 3159 | ++matchlen; 3160 | if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) 3161 | return 1; 3162 | } 3163 | } else { 3164 | // if nothing immediately following 3165 | if (matchlen == nlen) 3166 | return 1; 3167 | } 3168 | } 3169 | } 3170 | 3171 | // @TODO handle other encodings 3172 | } 3173 | } 3174 | return 0; 3175 | } 3176 | 3177 | static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) 3178 | { 3179 | stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); 3180 | stbtt_uint32 nm,hd; 3181 | if (!stbtt__isfont(fc+offset)) return 0; 3182 | 3183 | // check italics/bold/underline flags in macStyle... 3184 | if (flags) { 3185 | hd = stbtt__find_table(fc, offset, "head"); 3186 | if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; 3187 | } 3188 | 3189 | nm = stbtt__find_table(fc, offset, "name"); 3190 | if (!nm) return 0; 3191 | 3192 | if (flags) { 3193 | // if we checked the macStyle flags, then just check the family and ignore the subfamily 3194 | if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; 3195 | if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; 3196 | if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; 3197 | } else { 3198 | if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; 3199 | if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; 3200 | if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; 3201 | } 3202 | 3203 | return 0; 3204 | } 3205 | 3206 | STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) 3207 | { 3208 | stbtt_int32 i; 3209 | for (i=0;;++i) { 3210 | stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); 3211 | if (off < 0) return off; 3212 | if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) 3213 | return off; 3214 | } 3215 | } 3216 | 3217 | #endif // STB_TRUETYPE_IMPLEMENTATION 3218 | 3219 | 3220 | // FULL VERSION HISTORY 3221 | // 3222 | // 1.11 (2016-04-02) fix unused-variable warning 3223 | // 1.10 (2016-04-02) allow user-defined fabs() replacement 3224 | // fix memory leak if fontsize=0.0 3225 | // fix warning from duplicate typedef 3226 | // 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges 3227 | // 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges 3228 | // 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; 3229 | // allow PackFontRanges to pack and render in separate phases; 3230 | // fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); 3231 | // fixed an assert() bug in the new rasterizer 3232 | // replace assert() with STBTT_assert() in new rasterizer 3233 | // 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) 3234 | // also more precise AA rasterizer, except if shapes overlap 3235 | // remove need for STBTT_sort 3236 | // 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC 3237 | // 1.04 (2015-04-15) typo in example 3238 | // 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes 3239 | // 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ 3240 | // 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match 3241 | // non-oversampled; STBTT_POINT_SIZE for packed case only 3242 | // 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling 3243 | // 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) 3244 | // 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID 3245 | // 0.8b (2014-07-07) fix a warning 3246 | // 0.8 (2014-05-25) fix a few more warnings 3247 | // 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back 3248 | // 0.6c (2012-07-24) improve documentation 3249 | // 0.6b (2012-07-20) fix a few more warnings 3250 | // 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, 3251 | // stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty 3252 | // 0.5 (2011-12-09) bugfixes: 3253 | // subpixel glyph renderer computed wrong bounding box 3254 | // first vertex of shape can be off-curve (FreeSans) 3255 | // 0.4b (2011-12-03) fixed an error in the font baking example 3256 | // 0.4 (2011-12-01) kerning, subpixel rendering (tor) 3257 | // bugfixes for: 3258 | // codepoint-to-glyph conversion using table fmt=12 3259 | // codepoint-to-glyph conversion using table fmt=4 3260 | // stbtt_GetBakedQuad with non-square texture (Zer) 3261 | // updated Hello World! sample to use kerning and subpixel 3262 | // fixed some warnings 3263 | // 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) 3264 | // userdata, malloc-from-userdata, non-zero fill (stb) 3265 | // 0.2 (2009-03-11) Fix unsigned/signed char warnings 3266 | // 0.1 (2009-03-09) First public release 3267 | // 3268 | --------------------------------------------------------------------------------