├── .gitignore ├── build.fox ├── demo ├── main.c ├── tigr.c └── tigr.h ├── license ├── nativefonts.h ├── readme.md ├── screenshot_osx.png ├── screenshot_win10.png └── src ├── dwrite_c.h ├── nativefonts_coretext.c └── nativefonts_dwrite.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .ninja_* 2 | build.* 3 | !build.fox 4 | build/ 5 | Makefile 6 | -------------------------------------------------------------------------------- /build.fox: -------------------------------------------------------------------------------- 1 | #variation = release 2 | 3 | filter system: Windows 4 | ldflags += /SUBSYSTEM:WINDOWS 5 | libs += Opengl32 Gdi32 d3d9 User32 Shell32 6 | filter system: Darwin 7 | frameworks += OpenGL Cocoa CoreText 8 | 9 | includedirs += . demo src 10 | defines += TIGR_DO_NOT_PRESERVE_WINDOW_POSITION 11 | 12 | build objects(build/**/*): auto **/*.c | **/*.h 13 | build objects(build/**/*): auto **/*.cpp | **/*.h 14 | build objects(build/**/*): cc **/*.m | **/*.h 15 | build application(build/demo_app): auto objects(build/**/*) 16 | -------------------------------------------------------------------------------- /demo/main.c: -------------------------------------------------------------------------------- 1 | // demo of nativefonts 2 | 3 | #include "tigr.h" 4 | #include 5 | 6 | const char * sample1 = 7 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras convallis elementum nunc vitae malesuada. Sed vitae finibus nunc. Mauris ipsum odio, imperdiet id ultricies nec, tristique sed mauris. Integer venenatis lorem id lectus lacinia pulvinar. Maecenas molestie ligula ultrices eros sodales, vel hendrerit elit lacinia. Suspendisse in magna dui. Quisque a arcu nisl.\n" 8 | "\n" 9 | "Phasellus elementum aliquet dolor non convallis. Pellentesque et ligula ante. Quisque eu tristique eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In at ex a metus dapibus hendrerit id id leo. Maecenas sagittis ipsum ac nibh sagittis, vel dictum lectus scelerisque. Phasellus quis lacus in dui placerat hendrerit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque mattis tincidunt risus eget venenatis. Ut pulvinar vehicula neque, et mattis libero molestie ut. Ut quis ornare orci. Ut eu arcu nisl. Vestibulum suscipit iaculis augue sed ultrices.\n" 10 | "\n" 11 | "Donec vitae ex felis. Vestibulum ornare urna non magna imperdiet, et ornare lacus sodales. Pellentesque nibh ligula, dapibus a dapibus nec, gravida at ligula. Vestibulum vitae tellus dignissim elit consectetur tincidunt. In ultricies velit ac efficitur auctor. Cras ut facilisis enim. Integer blandit turpis quis interdum finibus. Phasellus vitae imperdiet odio, sit amet varius nisl. Quisque sollicitudin mauris nisi, eget imperdiet augue dictum in. Nunc aliquet, dolor quis dapibus feugiat, metus mi accumsan nisi, at dictum eros ante nec erat. Phasellus tempor massa non felis rutrum tempus. Fusce ac quam nulla.\n" 12 | "\n" 13 | "Nulla diam metus, cursus eget efficitur consequat, feugiat id est. Integer eu odio sed ex pulvinar vehicula egestas eu dui. Nullam non ante molestie, iaculis dui in, dictum tortor. Phasellus venenatis lacus sed quam dignissim, sed finibus leo ullamcorper. Integer molestie ac sapien vitae ultricies. Mauris a quam nibh. Nam maximus in quam non rutrum. Cras lacinia, lectus eget facilisis semper, enim nisl tempus mi, at malesuada lorem quam nec ante. Duis vestibulum diam libero, dapibus facilisis lorem tempor eu. Duis imperdiet, justo sit amet bibendum ultrices, arcu nisl pretium mauris, in feugiat mauris mi nec dui.\n" 14 | "\n" 15 | "Aliquam ac justo ut eros sagittis accumsan eget semper orci. Vestibulum vehicula quam eget nibh rhoncus pharetra. Donec quis sem rutrum, gravida tortor in, varius turpis. Morbi aliquet ex nec pulvinar ullamcorper. Nam sed cursus ante, ut dapibus turpis. Nunc eu lacus quis felis feugiat lacinia. Nulla placerat id erat et rutrum. Etiam dignissim, odio ut suscipit facilisis, ante purus rutrum tortor, eget ornare nibh lectus sit amet nibh. Sed facilisis orci sed ultricies ultrices. Morbi non imperdiet arcu.\n" 16 | "\n" 17 | "Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed posuere venenatis nulla. Pellentesque commodo tellus non tellus lobortis, ut consequat eros scelerisque. Nunc at nunc lorem. Integer justo nisi, ullamcorper varius vulputate quis, dapibus et arcu. Donec sollicitudin elit in magna mattis, a interdum dolor vestibulum. Morbi at ante eros. Nulla et sodales felis. Etiam sagittis libero vitae massa finibus convallis. Integer interdum pretium augue vitae ullamcorper. Aliquam luctus scelerisque lorem vel egestas. Ut tristique turpis sed est ultricies, id commodo velit placerat. Aenean a mi ac odio ornare euismod.\n" 18 | "\n" 19 | "Aenean quis finibus odio, eget dapibus nisl. Maecenas accumsan dolor sed enim tempus vestibulum. Nam semper, nisl eu dapibus sodales, massa metus interdum velit, sed pharetra turpis ipsum in nisl. Cras tincidunt augue quis mi vehicula, sed iaculis magna convallis. Integer sed neque ultricies, mollis magna at, fermentum metus. Curabitur facilisis aliquet ultrices. Integer eget maximus elit. Sed viverra, lorem id ultricies scelerisque, mi nunc finibus ligula, nec porta mi leo at eros. In nec arcu eget sem laoreet ornare. Proin imperdiet turpis erat, ut varius elit molestie et. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed lacinia quam vitae volutpat volutpat.\n" 20 | "\n" 21 | "Nunc at quam a sem faucibus pulvinar at eget eros. Sed consequat elit et erat congue commodo. Integer vel dolor suscipit, tincidunt lorem nec, dictum nulla. Nullam suscipit risus tortor, eget sagittis justo tristique id. Mauris mattis tellus vel mi tincidunt porta. Praesent finibus velit ligula, in feugiat sem vulputate quis. Duis massa magna, vulputate quis nisi id, molestie ultricies augue. Quisque ac elementum nisi, eu dapibus arcu. Duis eleifend purus egestas, scelerisque nulla id, eleifend libero. Suspendisse sem odio, facilisis id feugiat sed, dignissim vel elit. Morbi ornare sit amet massa sit amet pharetra. Phasellus at tellus risus. Sed hendrerit, ex volutpat placerat faucibus, eros elit ornare nisi, vitae viverra arcu libero non nulla. Aliquam hendrerit ipsum lacus, quis lobortis nibh rutrum sed. Proin eleifend nisi a tincidunt aliquet.\n" 22 | "\n" 23 | "Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed quis felis imperdiet, auctor risus eu, pharetra sem. Vivamus et feugiat sem. Sed lobortis consectetur euismod. Aliquam id iaculis risus. Curabitur varius, justo nec efficitur venenatis, augue quam tincidunt arcu, ut elementum arcu velit eu nisl. Sed luctus nibh ac turpis posuere sollicitudin.\n" 24 | "\n" 25 | "Quisque mi purus, consequat non massa vitae, efficitur tempor sapien. Nunc nisl ipsum, vehicula venenatis aliquet at, finibus cursus felis. In sollicitudin pretium lorem a tincidunt. Cras imperdiet vel risus a lacinia. Phasellus eget dignissim lacus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus aliquet, velit in molestie vehicula, mi neque hendrerit diam, ut tristique turpis lorem in augue. Aliquam in orci convallis, efficitur augue et, faucibus ex. Curabitur ullamcorper tellus eu turpis maximus lobortis. Aenean sodales lorem eros, eu consectetur metus faucibus et. Cras faucibus ipsum lorem, sit amet posuere purus mollis varius. Pellentesque porta elementum ex, vel ullamcorper odio porttitor at. Nulla ac vestibulum urna, eget maximus erat. Nam ullamcorper fermentum erat ut convallis. Vivamus id porta erat, vitae elementum est.\n" 26 | "\n" 27 | "Nulla sagittis, orci eget convallis consequat, nisl odio luctus turpis, non sodales enim libero id risus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam maximus eleifend neque, nec viverra neque rutrum quis. Praesent lobortis ex feugiat arcu rutrum imperdiet. Suspendisse potenti. Quisque tempus sagittis fermentum. Quisque id tincidunt nisi. Aliquam massa augue, vulputate non efficitur in, suscipit id est.\n" 28 | "\n" 29 | "Praesent cursus hendrerit arcu eu mattis. Nunc ullamcorper dapibus dignissim. Proin sed faucibus risus. Donec varius sit amet sapien ultricies feugiat. Pellentesque mattis laoreet viverra. Aliquam quis scelerisque eros. Sed vitae mi a diam rutrum condimentum. In finibus quis neque in tristique. Donec nec posuere urna. Phasellus lacinia ornare diam ac pellentesque. Etiam in iaculis nunc. Sed ac tortor pulvinar, tempus eros vel, dictum metus. Sed dui dolor, scelerisque ut tempus venenatis, suscipit sit amet massa.\n" 30 | "\n" 31 | "Maecenas molestie ante ac sapien pretium hendrerit. Quisque et neque ligula. Nulla gravida diam lacus, sed bibendum ipsum gravida convallis. Integer ut diam ut ex mollis porta. Nulla facilisi. Nunc ipsum lacus, mollis sit amet sem at, fermentum egestas enim. Ut ac posuere risus. Integer eu ligula tempus, congue metus ac, tincidunt lorem. Quisque ut augue eget mauris dapibus dictum ornare nec metus. Morbi et fringilla eros, id varius lectus. Nunc eu risus lacus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.\n" 32 | "\n" 33 | "Nulla volutpat varius consectetur. Nulla maximus vehicula scelerisque. Curabitur nec lorem nec felis posuere scelerisque sit amet vitae libero. Integer molestie hendrerit dictum. Integer velit sapien, lobortis eget hendrerit vel, viverra eget eros. Vivamus placerat quis magna et venenatis. Aliquam feugiat at urna laoreet ornare. In iaculis erat vitae sapien blandit, ac accumsan ante tempor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.\n" 34 | "\n" 35 | "Nullam suscipit aliquet urna eu commodo. Praesent in sem vel odio ultrices bibendum a posuere sem. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras ut lorem sagittis arcu congue faucibus non vel tellus. Quisque sagittis, est dapibus suscipit lacinia, leo sem semper sem, quis pretium lacus urna vitae sapien. Aliquam in facilisis orci. Morbi interdum magna posuere rhoncus aliquam. Sed viverra pulvinar tortor, tempus consequat dui tempor vel. Phasellus orci dolor, malesuada ac augue sit amet, interdum consectetur enim. In venenatis elit sit amet est ultrices bibendum. Donec et dictum ipsum. Nunc commodo mattis dui eget malesuada. Nam tincidunt sed nulla in vulputate. Nullam vestibulum libero blandit ipsum euismod eleifend. Phasellus tristique urna sit amet risus condimentum aliquet.\n"; 36 | 37 | char sample2[256] = {0}; 38 | 39 | void gen_sample2_str() 40 | { 41 | for(size_t i = 0; i < sizeof(sample2) - 1; ++i) 42 | sample2[i] = rand() % ('z' - 'a' + 1) + 'a'; 43 | sample2[sizeof(sample2) - 1] = '\0'; 44 | } 45 | 46 | int main(int argc, char *argv[]) 47 | { 48 | Tigr * screen = tigrWindow(800, 600, "Native fonts demo", TIGR_RETINA); 49 | Tigr * bitmap1 = tigrBitmap(screen->w, screen->h); 50 | Tigr * bitmap2 = tigrBitmap(screen->w, screen->h); 51 | 52 | nf_font_params_t params = {0}; 53 | params.size_in_pt = screen->w > 800 ? 26.0f : 13.0f; // check for retina 54 | nf_font_t font = nf_font("Arial", params); 55 | 56 | nf_feature_t features[2]; 57 | features[0].type = NF_FEATURE_ITALIC; 58 | features[0].range.start = 6; 59 | features[0].range.end = 10; 60 | features[1].type = NF_FEATURE_UNDERLINE; 61 | features[1].range.start = 12; 62 | features[1].range.end = 16; 63 | 64 | while(!tigrClosed(screen) && !tigrKeyDown(screen, TK_ESCAPE)) 65 | { 66 | tigrClear(screen, tigrRGB(0x80, 0x90, 0xa0)); 67 | 68 | gen_sample2_str(); 69 | 70 | int base_y = 35; 71 | 72 | tigrTime(); 73 | tigrPrint(screen, tfont, 5, base_y, tigrRGB(0xff, 0xff, 0xff), sample2); 74 | float time1 = tigrTime(); 75 | 76 | tigrTime(); 77 | nf_aabb_t aabb1; 78 | if(nf_print(bitmap1->pix, bitmap1->w, bitmap1->h, font, 79 | features, 2, &aabb1, sample1) < 0) 80 | return -1; 81 | float time2 = tigrTime(); 82 | 83 | tigrTime(); 84 | nf_aabb_t aabb2; 85 | if(nf_print(bitmap2->pix, bitmap2->w, bitmap2->h, 86 | font, NULL, 0, &aabb2, sample2) < 0) 87 | return -1; 88 | float time3 = tigrTime(); 89 | 90 | // pma 91 | tigrBlitTint2(screen, bitmap2, 5, base_y + 20, 92 | aabb2.x, aabb2.y, aabb2.w, aabb2.h, 93 | tigrRGBA(0xff,0xff,0xff,(unsigned char)(1.0f*255))); 94 | tigrBlitTint2(screen, bitmap1, 5, base_y + 60, 95 | aabb1.x, aabb1.y, aabb1.w, aabb1.h, 96 | tigrRGBA(0xff,0xff,0xff,(unsigned char)(1.0f*255))); 97 | // not pma 98 | //tigrBlit(screen, bitmap1, 0, 0, 0, 0, bitmap1->w, bitmap1->h); 99 | 100 | tigrPrint(screen, tfont, 5, 5, tigrRGB(0xff, 0xff, 0xff), 101 | "tigr font took %f ms, long text took %f ms, short text took %f ms", 102 | time1 * 1000.0f, time2 * 1000.0f, time3 * 1000.0f); 103 | 104 | tigrUpdate(screen); 105 | } 106 | 107 | nf_free(font); 108 | 109 | tigrFree(bitmap1); 110 | tigrFree(bitmap2); 111 | tigrFree(screen); 112 | 113 | return 0; 114 | } 115 | -------------------------------------------------------------------------------- /demo/tigr.c: -------------------------------------------------------------------------------- 1 | //////// Start of inlined file: tigr_amalgamated.c //////// 2 | 3 | 4 | #include "tigr.h" 5 | 6 | //////// Start of inlined file: tigr_bitmaps.c //////// 7 | 8 | //////// Start of inlined file: tigr_internal.h //////// 9 | 10 | // can't use pragma once here because this file probably will endup in .c 11 | #ifndef __TIGR_INTERNAL_H__ 12 | #define __TIGR_INTERNAL_H__ 13 | 14 | #define _CRT_SECURE_NO_WARNINGS NOPE 15 | 16 | // Creates a new bitmap, with extra payload bytes. 17 | Tigr *tigrBitmap2(int w, int h, int extra); 18 | 19 | // Resizes an existing bitmap. 20 | void tigrResize(Tigr *bmp, int w, int h); 21 | 22 | // Calculates the biggest scale that a bitmap can fit into an area at. 23 | int tigrCalcScale(int bmpW, int bmpH, int areaW, int areaH); 24 | 25 | // Calculates a new scale, taking minimum-scale flags into account. 26 | int tigrEnforceScale(int scale, int flags); 27 | 28 | // Calculates the correct position for a bitmap to fit into a window. 29 | void tigrPosition(Tigr *bmp, int scale, int windowW, int windowH, int out[4]); 30 | 31 | // ---------------------------------------------------------- 32 | #ifdef _WIN32 33 | #define WIN32_LEAN_AND_MEAN 34 | #include 35 | #endif 36 | 37 | #ifdef TIGR_GAPI_D3D9 38 | #include 39 | typedef struct { 40 | int lost; 41 | IDirect3DDevice9 *dev; 42 | D3DPRESENT_PARAMETERS params; 43 | IDirect3DTexture9 *sysTex[2], *vidTex[2]; 44 | IDirect3DVertexShader9 *vs; 45 | IDirect3DPixelShader9 *ps; 46 | } D3D9Stuff; 47 | #endif 48 | 49 | #ifdef TIGR_GAPI_GL 50 | #ifdef _WIN32 51 | #include 52 | #else 53 | #include 54 | #endif 55 | typedef struct { 56 | #ifdef _WIN32 57 | HGLRC hglrc; 58 | HDC dc; 59 | #endif 60 | GLuint tex[2]; 61 | GLuint vao; 62 | GLuint program; 63 | GLuint uniform_projection; 64 | GLuint uniform_model; 65 | GLuint uniform_parameters; 66 | int gl_legacy; 67 | } GLStuff; 68 | #endif 69 | 70 | typedef struct { 71 | int shown, closed; 72 | #ifdef TIGR_GAPI_D3D9 73 | D3D9Stuff d3d9; 74 | #endif 75 | #ifdef TIGR_GAPI_GL 76 | GLStuff gl; 77 | #endif 78 | 79 | #ifdef _WIN32 80 | wchar_t *wtitle; 81 | DWORD dwStyle; 82 | RECT oldPos; 83 | #endif 84 | #ifdef __APPLE__ 85 | void *glContext; 86 | #endif 87 | 88 | Tigr *widgets; 89 | int widgetsWanted; 90 | unsigned char widgetAlpha; 91 | float widgetsScale; 92 | 93 | int hblur, vblur; 94 | float scanlines, contrast; 95 | 96 | int flags; 97 | int scale; 98 | int pos[4]; 99 | int lastChar; 100 | char keys[256], prev[256]; 101 | #ifdef __APPLE__ 102 | int mouseButtons; 103 | #endif 104 | } TigrInternal; 105 | // ---------------------------------------------------------- 106 | 107 | TigrInternal *tigrInternal(Tigr *bmp); 108 | 109 | void tigrGAPICreate(Tigr *bmp); 110 | void tigrGAPIDestroy(Tigr *bmp); 111 | void tigrGAPIBegin(Tigr *bmp); 112 | void tigrGAPIEnd(Tigr *bmp); 113 | void tigrGAPIResize(Tigr *bmp, int width, int height); 114 | void tigrGAPIPresent(Tigr *bmp, int w, int h); 115 | 116 | #endif 117 | 118 | 119 | //////// End of inlined file: tigr_internal.h //////// 120 | 121 | #include 122 | #include 123 | 124 | // Expands 0-255 into 0-256 125 | #define EXPAND(X) ((X) + ((X) > 0)) 126 | 127 | #define CLIP0(X, X2, W) if (X < 0) { W += X; X2 -= X; X = 0; } 128 | #define CLIP1(X, DW, W) if (X + W > DW) W = DW - X; 129 | #define CLIP() \ 130 | CLIP0(dx, sx, w); \ 131 | CLIP0(dy, sy, h); \ 132 | CLIP0(sx, dx, w); \ 133 | CLIP0(sy, dy, h); \ 134 | CLIP1(dx, dst->w, w); \ 135 | CLIP1(dy, dst->h, h); \ 136 | CLIP1(sx, src->w, w); \ 137 | CLIP1(sy, src->h, h); \ 138 | if (w <= 0 || h <= 0) \ 139 | return 140 | 141 | 142 | Tigr *tigrBitmap2(int w, int h, int extra) 143 | { 144 | Tigr *tigr = (Tigr *)calloc(1, sizeof(Tigr) + extra); 145 | tigr->w = w; 146 | tigr->h = h; 147 | tigr->pix = (TPixel *)calloc(w*h, sizeof(TPixel)); 148 | return tigr; 149 | } 150 | 151 | Tigr *tigrBitmap(int w, int h) 152 | { 153 | return tigrBitmap2(w, h, 0); 154 | } 155 | 156 | void tigrResize(Tigr *bmp, int w, int h) 157 | { 158 | int y, cw, ch; 159 | TPixel *newpix = (TPixel *)calloc(w*h, sizeof(TPixel)); 160 | cw = (w < bmp->w) ? w : bmp->w; 161 | ch = (h < bmp->h) ? h : bmp->h; 162 | 163 | // Copy any old data across. 164 | for (y=0;ypix+y*bmp->w, cw*sizeof(TPixel)); 166 | 167 | free(bmp->pix); 168 | bmp->pix = newpix; 169 | bmp->w = w; 170 | bmp->h = h; 171 | } 172 | 173 | int tigrCalcScale(int bmpW, int bmpH, int areaW, int areaH) 174 | { 175 | // We want it as big as possible in the window, but still 176 | // maintaining the correct aspect ratio, and always 177 | // having an integer pixel size. 178 | int scale = 0; 179 | for(;;) 180 | { 181 | scale++; 182 | if (bmpW*scale > areaW || bmpH*scale > areaH) 183 | { 184 | scale--; 185 | break; 186 | } 187 | } 188 | return (scale > 1) ? scale : 1; 189 | } 190 | 191 | int tigrEnforceScale(int scale, int flags) 192 | { 193 | if ((flags & TIGR_4X) && scale < 4) scale = 4; 194 | if ((flags & TIGR_3X) && scale < 3) scale = 3; 195 | if ((flags & TIGR_2X) && scale < 2) scale = 2; 196 | return scale; 197 | } 198 | 199 | void tigrPosition(Tigr *bmp, int scale, int windowW, int windowH, int out[4]) 200 | { 201 | // Center the image on screen at this scale. 202 | out[0] = (windowW - bmp->w*scale) / 2; 203 | out[1] = (windowH - bmp->h*scale) / 2; 204 | out[2] = out[0] + bmp->w*scale; 205 | out[3] = out[1] + bmp->h*scale; 206 | } 207 | 208 | void tigrClear(Tigr *bmp, TPixel color) 209 | { 210 | int count = bmp->w * bmp->h; 211 | int n; 212 | for (n=0;npix[n] = color; 214 | } 215 | 216 | void tigrFill(Tigr *bmp, int x, int y, int w, int h, TPixel color) 217 | { 218 | TPixel *td; 219 | int dt, i; 220 | 221 | if (x < 0) { w += x; x = 0; } 222 | if (y < 0) { h += y; y = 0; } 223 | if (x + w > bmp->w) { w = bmp->w - x; } 224 | if (y + h > bmp->h) { h = bmp->h - y; } 225 | if (w <= 0 || h <= 0) 226 | return; 227 | 228 | td = &bmp->pix[y*bmp->w + x]; 229 | dt = bmp->w; 230 | do { 231 | for (i=0;i -dy) { err -= dy; x0 += sx; } 252 | if (e2 < dx) { err += dx; y0 += sy; } 253 | } 254 | } 255 | 256 | void tigrRect(Tigr *bmp, int x, int y, int w, int h, TPixel color) 257 | { 258 | int x1, y1; 259 | if (w <= 0 || h <= 0) 260 | return; 261 | 262 | x1 = x + w-1; 263 | y1 = y + h-1; 264 | tigrLine(bmp, x, y, x1, y, color); 265 | tigrLine(bmp, x1, y, x1, y1, color); 266 | tigrLine(bmp, x1, y1, x, y1, color); 267 | tigrLine(bmp, x, y1, x, y, color); 268 | } 269 | 270 | TPixel tigrGet(Tigr *bmp, int x, int y) 271 | { 272 | TPixel empty = { 0,0,0,0 }; 273 | if (x >= 0 && y >= 0 && x < bmp->w && y < bmp->h) 274 | return bmp->pix[y*bmp->w+x]; 275 | return empty; 276 | } 277 | 278 | void tigrPlot(Tigr *bmp, int x, int y, TPixel pix) 279 | { 280 | int xa, i, a; 281 | if (x >= 0 && y >= 0 && x < bmp->w && y < bmp->h) 282 | { 283 | xa = EXPAND(pix.a); 284 | i = y*bmp->w+x; 285 | 286 | a = xa * EXPAND(pix.a); 287 | bmp->pix[i].r += (unsigned char)((pix.r - bmp->pix[i].r)*a >> 16); 288 | bmp->pix[i].g += (unsigned char)((pix.g - bmp->pix[i].g)*a >> 16); 289 | bmp->pix[i].b += (unsigned char)((pix.b - bmp->pix[i].b)*a >> 16); 290 | bmp->pix[i].a += (unsigned char)((pix.a - bmp->pix[i].a)*a >> 16); 291 | } 292 | } 293 | 294 | void tigrBlit(Tigr *dst, Tigr *src, int dx, int dy, int sx, int sy, int w, int h) 295 | { 296 | TPixel *td, *ts; 297 | int st, dt; 298 | CLIP(); 299 | 300 | ts = &src->pix[sy*src->w + sx]; 301 | td = &dst->pix[dy*dst->w + dx]; 302 | st = src->w; 303 | dt = dst->w; 304 | do { 305 | memcpy(td, ts, w*sizeof(TPixel)); 306 | ts += st; 307 | td += dt; 308 | } while(--h); 309 | } 310 | 311 | void tigrBlitTint(Tigr *dst, Tigr *src, int dx, int dy, int sx, int sy, int w, int h, TPixel tint) 312 | { 313 | TPixel *td, *ts; 314 | int x, st, dt, xr,xg,xb,xa; 315 | CLIP(); 316 | 317 | xr = EXPAND(tint.r); 318 | xg = EXPAND(tint.g); 319 | xb = EXPAND(tint.b); 320 | xa = EXPAND(tint.a); 321 | 322 | ts = &src->pix[sy*src->w + sx]; 323 | td = &dst->pix[dy*dst->w + dx]; 324 | st = src->w; 325 | dt = dst->w; 326 | do { 327 | for (x=0;x> 8; 330 | unsigned g = (xg * ts[x].g) >> 8; 331 | unsigned b = (xb * ts[x].b) >> 8; 332 | unsigned a = xa * EXPAND(ts[x].a); 333 | td[x].r += (unsigned char)((r - td[x].r)*a >> 16); 334 | td[x].g += (unsigned char)((g - td[x].g)*a >> 16); 335 | td[x].b += (unsigned char)((b - td[x].b)*a >> 16); 336 | td[x].a += (unsigned char)((ts[x].a - td[x].a)*a >> 16); 337 | } 338 | ts += st; 339 | td += dt; 340 | } while(--h); 341 | } 342 | 343 | void tigrBlitTint2(Tigr *dst, Tigr *src, int dx, int dy, int sx, int sy, int w, int h, TPixel tint) 344 | { 345 | TPixel *td, *ts; 346 | int x, st, dt, xr,xg,xb,xa; 347 | CLIP(); 348 | 349 | xr = EXPAND(tint.r); 350 | xg = EXPAND(tint.g); 351 | xb = EXPAND(tint.b); 352 | xa = EXPAND(tint.a); 353 | 354 | ts = &src->pix[sy*src->w + sx]; 355 | td = &dst->pix[dy*dst->w + dx]; 356 | st = src->w; 357 | dt = dst->w; 358 | do { 359 | for (x=0;x> 8; 362 | unsigned g = (xg * ts[x].g) >> 8; 363 | unsigned b = (xb * ts[x].b) >> 8; 364 | unsigned a = xa * EXPAND(ts[x].a); 365 | td[x].r += (unsigned char)(r - (td[x].r * a >> 16)); 366 | td[x].g += (unsigned char)(g - (td[x].g * a >> 16)); 367 | td[x].b += (unsigned char)(b - (td[x].b * a >> 16)); 368 | td[x].a += (unsigned char)((ts[x].a - td[x].a)*a >> 16); 369 | } 370 | ts += st; 371 | td += dt; 372 | } while(--h); 373 | } 374 | 375 | void tigrBlitAlpha(Tigr *dst, Tigr *src, int dx, int dy, int sx, int sy, int w, int h, float alpha) 376 | { 377 | alpha = (alpha < 0) ? 0 : (alpha > 1 ? 1 : alpha); 378 | tigrBlitTint(dst, src, dx, dy, sx, sy, w, h, tigrRGBA(0xff,0xff,0xff,(unsigned char)(alpha*255))); 379 | } 380 | 381 | #undef CLIP0 382 | #undef CLIP1 383 | #undef CLIP 384 | 385 | //////// End of inlined file: tigr_bitmaps.c //////// 386 | 387 | //////// Start of inlined file: tigr_loadpng.c //////// 388 | 389 | //#include "tigr_internal.h" 390 | #include 391 | #include 392 | #include 393 | 394 | typedef struct { 395 | const unsigned char *p, *end; 396 | } PNG; 397 | 398 | static unsigned get32(const unsigned char *v) 399 | { 400 | return (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | v[3]; 401 | } 402 | 403 | static const unsigned char *find(PNG *png, const char *chunk, unsigned minlen) 404 | { 405 | const unsigned char *start; 406 | while (png->p < png->end) 407 | { 408 | unsigned len = get32(png->p+0); 409 | start = png->p; 410 | png->p += len + 12; 411 | if (memcmp(start+4, chunk, 4) == 0 && len >= minlen && png->p <= png->end) 412 | return start+8; 413 | } 414 | 415 | return NULL; 416 | } 417 | 418 | static unsigned char paeth(unsigned char a, unsigned char b, unsigned char c) 419 | { 420 | int p = a + b - c; 421 | int pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); 422 | return (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c; 423 | } 424 | 425 | static int unfilter(int w, int h, int bpp, unsigned char *raw) 426 | { 427 | int len = w * bpp, x, y; 428 | unsigned char *prev = raw; 429 | for (y=0;yw+1)*bmp->h * bpp; } 481 | 482 | static Tigr *tigrLoadPng(PNG *png) 483 | { 484 | const unsigned char *ihdr, *idat, *plte, *first; 485 | int depth, ctype, bpp; 486 | int datalen = 0; 487 | unsigned char *data = NULL, *out; 488 | Tigr *bmp = NULL; 489 | 490 | CHECK(memcmp(png->p, "\211PNG\r\n\032\n", 8) == 0); // PNG signature 491 | png->p += 8; 492 | first = png->p; 493 | 494 | // Read IHDR 495 | ihdr = find(png, "IHDR", 13); 496 | CHECK(ihdr); 497 | depth = ihdr[8]; 498 | ctype = ihdr[9]; 499 | switch (ctype) { 500 | case 0: bpp = 1; break; // greyscale 501 | case 2: bpp = 3; break; // RGB 502 | case 3: bpp = 1; break; // paletted 503 | case 4: bpp = 2; break; // grey+alpha 504 | case 6: bpp = 4; break; // RGBA 505 | default: FAIL(); 506 | } 507 | 508 | // Allocate bitmap (+1 width to save room for stupid PNG filter bytes) 509 | bmp = tigrBitmap(get32(ihdr+0) + 1, get32(ihdr+4)); 510 | CHECK(bmp); 511 | bmp->w--; 512 | 513 | // We don't support non-8bpp, interlacing, or wacky filter types. 514 | CHECK(depth == 8 && ihdr[10] == 0 && ihdr[11] == 0 && ihdr[12] == 0); 515 | 516 | // Join IDAT chunks. 517 | for (idat=find(png, "IDAT", 0); idat; idat=find(png, "IDAT", 0)) 518 | { 519 | unsigned len = get32(idat-8); 520 | data = (unsigned char *)realloc(data, datalen + len); 521 | if (!data) 522 | break; 523 | 524 | memcpy(data+datalen, idat, len); 525 | datalen += len; 526 | } 527 | 528 | // Find palette. 529 | png->p = first; 530 | plte = find(png, "PLTE", 0); 531 | 532 | CHECK(data && datalen >= 6); 533 | CHECK((data[0] & 0x0f) == 0x08 // compression method (RFC 1950) 534 | && (data[0] & 0xf0) <= 0x70 // window size 535 | && (data[1] & 0x20) == 0); // preset dictionary present 536 | 537 | out = (unsigned char *)bmp->pix + outsize(bmp, 4) - outsize(bmp, bpp); 538 | CHECK(tigrInflate(out, outsize(bmp, bpp), data+2, datalen-6)); 539 | CHECK(unfilter(bmp->w, bmp->h, bpp, out)); 540 | 541 | if (ctype == 3) { 542 | CHECK(plte); 543 | depalette(bmp->w, bmp->h, out, bmp->pix, plte); 544 | } else { 545 | convert(bpp, bmp->w, bmp->h, out, bmp->pix); 546 | } 547 | 548 | free(data); 549 | return bmp; 550 | 551 | err: 552 | if (data) free(data); 553 | if (bmp) tigrFree(bmp); 554 | return NULL; 555 | } 556 | 557 | #undef CHECK 558 | #undef FAIL 559 | 560 | Tigr *tigrLoadImageMem(const void *data, int length) 561 | { 562 | PNG png; 563 | png.p = (unsigned char *)data; 564 | png.end = (unsigned char *)data + length; 565 | return tigrLoadPng(&png); 566 | } 567 | 568 | Tigr *tigrLoadImage(const char *fileName) 569 | { 570 | int len; 571 | void *data; 572 | Tigr *bmp; 573 | 574 | data = tigrReadFile(fileName, &len); 575 | if (!data) 576 | return NULL; 577 | 578 | bmp = tigrLoadImageMem(data, len); 579 | free(data); 580 | return bmp; 581 | } 582 | 583 | 584 | //////// End of inlined file: tigr_loadpng.c //////// 585 | 586 | //////// Start of inlined file: tigr_savepng.c //////// 587 | 588 | //#include "tigr_internal.h" 589 | #include 590 | #include 591 | #include 592 | #include 593 | 594 | typedef struct { 595 | unsigned crc, adler, bits, prev, runlen; 596 | FILE *out; 597 | unsigned crcTable[256]; 598 | } Save; 599 | 600 | static const unsigned crctable[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 601 | 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; 602 | 603 | static void put(Save *s, unsigned v) 604 | { 605 | fputc(v, s->out); 606 | s->crc = (s->crc >> 4) ^ crctable[(s->crc & 15) ^ (v & 15)]; 607 | s->crc = (s->crc >> 4) ^ crctable[(s->crc & 15) ^ (v >> 4)]; 608 | } 609 | 610 | static void updateAdler(Save *s, unsigned v) 611 | { 612 | unsigned s1 = s->adler & 0xffff, s2 = (s->adler >> 16) & 0xffff; 613 | s1 = (s1 + v) % 65521; 614 | s2 = (s2 + s1) % 65521; 615 | s->adler = (s2 << 16) + s1; 616 | } 617 | 618 | static void put32(Save *s, unsigned v) 619 | { 620 | put(s, (v >> 24) & 0xff); 621 | put(s, (v >> 16) & 0xff); 622 | put(s, (v >> 8) & 0xff); 623 | put(s, v & 0xff); 624 | } 625 | 626 | void putbits(Save *s, unsigned data, unsigned bitcount) 627 | { 628 | while (bitcount--) 629 | { 630 | unsigned prev = s->bits; 631 | s->bits = (s->bits >> 1) | ((data & 1) << 7); 632 | data >>= 1; 633 | if (prev & 1) 634 | { 635 | put(s, s->bits); 636 | s->bits = 0x80; 637 | } 638 | } 639 | } 640 | 641 | void putbitsr(Save *s, unsigned data, unsigned bitcount) 642 | { 643 | while (bitcount--) 644 | putbits(s, data >> bitcount, 1); 645 | } 646 | 647 | static void begin(Save *s, const char *id, unsigned len) 648 | { 649 | put32(s, len); 650 | s->crc = 0xffffffff; 651 | put(s, id[0]); put(s, id[1]); put(s, id[2]); put(s, id[3]); 652 | } 653 | 654 | static void literal(Save *s, unsigned v) 655 | { 656 | // Encode a literal/length using the built-in tables. 657 | // Could do better with a custom table but whatever. 658 | if (v < 144) putbitsr(s, 0x030+v- 0, 8); 659 | else if (v < 256) putbitsr(s, 0x190+v-144, 9); 660 | else if (v < 280) putbitsr(s, 0x000+v-256, 7); 661 | else putbitsr(s, 0x0c0+v-280, 8); 662 | } 663 | 664 | static void encodelen(Save *s, unsigned code, unsigned bits, unsigned len) 665 | { 666 | literal(s, code + (len >> bits)); 667 | putbits(s, len, bits); 668 | putbits(s, 0, 5); 669 | } 670 | 671 | static void endrun(Save *s) 672 | { 673 | s->runlen--; 674 | literal(s, s->prev); 675 | 676 | if (s->runlen >= 67) encodelen(s, 277, 4, s->runlen - 67); 677 | else if (s->runlen >= 35) encodelen(s, 273, 3, s->runlen - 35); 678 | else if (s->runlen >= 19) encodelen(s, 269, 2, s->runlen - 19); 679 | else if (s->runlen >= 11) encodelen(s, 265, 1, s->runlen - 11); 680 | else if (s->runlen >= 3) encodelen(s, 257, 0, s->runlen - 3); 681 | else while (s->runlen--) literal(s, s->prev); 682 | } 683 | 684 | static void encodeByte(Save *s, unsigned char v) 685 | { 686 | updateAdler(s, v); 687 | 688 | // Simple RLE compression. We could do better by doing a search 689 | // to find matches, but this works pretty well TBH. 690 | if (s->prev == v && s->runlen < 115) 691 | { 692 | s->runlen++; 693 | } else { 694 | if (s->runlen) 695 | endrun(s); 696 | 697 | s->prev = v; 698 | s->runlen = 1; 699 | } 700 | } 701 | 702 | static void savePngHeader(Save *s, Tigr *bmp) 703 | { 704 | fwrite("\211PNG\r\n\032\n", 8, 1, s->out); 705 | begin(s, "IHDR", 13); 706 | put32(s, bmp->w); 707 | put32(s, bmp->h); 708 | put(s, 8); // bit depth 709 | put(s, 6); // RGBA 710 | put(s, 0); // compression (deflate) 711 | put(s, 0); // filter (standard) 712 | put(s, 0); // interlace off 713 | put32(s, ~s->crc); 714 | } 715 | 716 | static long savePngData(Save *s, Tigr *bmp, long dataPos) 717 | { 718 | int x, y; 719 | long dataSize; 720 | begin(s, "IDAT", 0); 721 | put(s, 0x08); // zlib compression method 722 | put(s, 0x1d); // zlib compression flags 723 | putbits(s, 3, 3); // zlib last block + fixed dictionary 724 | for (y=0;yh;y++) 725 | { 726 | TPixel *row = &bmp->pix[y*bmp->w]; 727 | TPixel prev = tigrRGBA(0, 0, 0, 0); 728 | 729 | encodeByte(s, 1); // sub filter 730 | for (x=0;xw;x++) 731 | { 732 | encodeByte(s, row[x].r - prev.r); 733 | encodeByte(s, row[x].g - prev.g); 734 | encodeByte(s, row[x].b - prev.b); 735 | encodeByte(s, row[x].a - prev.a); 736 | prev = row[x]; 737 | } 738 | } 739 | endrun(s); 740 | literal(s, 256); // terminator 741 | while (s->bits != 0x80) 742 | putbits(s, 0, 1); 743 | put32(s, s->adler); 744 | dataSize = (ftell(s->out) - dataPos) - 8; 745 | put32(s, ~s->crc); 746 | return dataSize; 747 | } 748 | 749 | int tigrSaveImage(const char *fileName, Tigr *bmp) 750 | { 751 | Save s; 752 | long dataPos, dataSize, err; 753 | 754 | // TODO - unicode? 755 | FILE *out = fopen(fileName, "wb"); 756 | if (!out) 757 | return 1; 758 | 759 | s.out = out; 760 | s.adler = 1; 761 | s.bits = 0x80; 762 | s.prev = 0xffff; 763 | s.runlen = 0; 764 | 765 | savePngHeader(&s, bmp); 766 | dataPos = ftell(s.out); 767 | dataSize = savePngData(&s, bmp, dataPos); 768 | 769 | // End chunk. 770 | begin(&s, "IEND", 0); 771 | put32(&s, ~s.crc); 772 | 773 | // Write back payload size. 774 | fseek(out, dataPos, SEEK_SET); 775 | put32(&s, dataSize); 776 | 777 | err = ferror(out); 778 | fclose(out); 779 | return !err; 780 | } 781 | 782 | //////// End of inlined file: tigr_savepng.c //////// 783 | 784 | //////// Start of inlined file: tigr_utils.c //////// 785 | 786 | //#include "tigr_internal.h" 787 | #include 788 | #include 789 | 790 | void *tigrReadFile(const char *fileName, int *length) 791 | { 792 | // TODO - unicode? 793 | FILE *file; 794 | char *data; 795 | size_t len; 796 | 797 | if (length) 798 | *length = 0; 799 | 800 | file = fopen(fileName, "rb"); 801 | if (!file) 802 | return NULL; 803 | 804 | fseek(file, 0, SEEK_END); 805 | len = ftell(file); 806 | fseek(file, 0, SEEK_SET); 807 | 808 | data = (char *)malloc(len+1); 809 | if (!data) 810 | { 811 | fclose(file); 812 | return NULL; 813 | } 814 | 815 | if (fread(data, 1, len, file) != len) { 816 | free(data); 817 | fclose(file); 818 | return NULL; 819 | } 820 | data[len] = '\0'; 821 | fclose(file); 822 | 823 | if (length) 824 | *length = len; 825 | 826 | return data; 827 | } 828 | 829 | // Reads a single UTF8 codepoint. 830 | const char *tigrDecodeUTF8(const char *text, int *cp) 831 | { 832 | unsigned char c = *text++; 833 | int extra = 0, min = 0; 834 | *cp = 0; 835 | if (c >= 0xf0) { *cp = c & 0x07; extra = 3; min = 0x10000; } 836 | else if (c >= 0xe0) { *cp = c & 0x0f; extra = 2; min = 0x800; } 837 | else if (c >= 0xc0) { *cp = c & 0x1f; extra = 1; min = 0x80; } 838 | else if (c >= 0x80) { *cp = 0xfffd; } 839 | else *cp = c; 840 | while (extra--) { 841 | c = *text++; 842 | if ((c & 0xc0) != 0x80) { *cp = 0xfffd; break; } 843 | (*cp) = ((*cp) << 6) | (c & 0x3f); 844 | } 845 | if (*cp < min) *cp = 0xfffd; 846 | return text; 847 | } 848 | 849 | char *tigrEncodeUTF8(char *text, int cp) 850 | { 851 | if (cp < 0 || cp > 0x10ffff) cp = 0xfffd; 852 | 853 | #define EMIT(X,Y,Z) *text++ = X | ((cp >> Y)&Z) 854 | if (cp < 0x80) { EMIT(0x00,0,0x7f); } 855 | else if (cp < 0x800) { EMIT(0xc0,6,0x1f); EMIT(0x80, 0, 0x3f); } 856 | else if (cp < 0x10000) { EMIT(0xe0,12,0xf); EMIT(0x80, 6, 0x3f); EMIT(0x80, 0, 0x3f); } 857 | else { EMIT(0xf0,18,0x7); EMIT(0x80, 12, 0x3f); EMIT(0x80, 6, 0x3f); EMIT(0x80, 0, 0x3f); } 858 | return text; 859 | #undef EMIT 860 | } 861 | 862 | void tigrSetPostFX(Tigr *bmp, int hblur, int vblur, float scanlines, float contrast) 863 | { 864 | TigrInternal *win = tigrInternal(bmp); 865 | win->hblur = hblur; 866 | win->vblur = vblur; 867 | win->scanlines = scanlines; 868 | win->contrast = contrast; 869 | } 870 | 871 | //////// End of inlined file: tigr_utils.c //////// 872 | 873 | //////// Start of inlined file: tigr_inflate.c //////// 874 | 875 | //#include "tigr_internal.h" 876 | #include 877 | #include 878 | 879 | typedef struct { 880 | unsigned bits, count; 881 | const unsigned char *in, *inend; 882 | unsigned char *out, *outend; 883 | jmp_buf jmp; 884 | unsigned litcodes[288], distcodes[32], lencodes[19]; 885 | int tlit, tdist, tlen; 886 | } State; 887 | 888 | #define FAIL() longjmp(s->jmp, 1) 889 | #define CHECK(X) if (!(X)) FAIL() 890 | 891 | // Built-in DEFLATE standard tables. 892 | static char order[] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; 893 | static char lenBits[29+2] = { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0, 0,0 }; 894 | static int lenBase[29+2] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 0,0 }; 895 | static char distBits[30+2] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13, 0,0 }; 896 | static int distBase[30+2] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577 }; 897 | 898 | // Table to bit-reverse a byte. 899 | static const unsigned char reverseTable[256] = { 900 | #define R2(n) n, n + 128, n + 64, n + 192 901 | #define R4(n) R2(n), R2(n + 32), R2(n + 16), R2(n + 48) 902 | #define R6(n) R4(n), R4(n + 8), R4(n + 4), R4(n + 12) 903 | R6(0), R6(2), R6(1), R6(3) 904 | }; 905 | 906 | static unsigned rev16(unsigned n) { return (reverseTable[n&0xff] << 8) | reverseTable[(n>>8)&0xff]; } 907 | 908 | static int bits(State *s, int n) 909 | { 910 | int v = s->bits & ((1 << n)-1); 911 | s->bits >>= n; 912 | s->count -= n; 913 | while (s->count < 16) 914 | { 915 | CHECK(s->in != s->inend); 916 | s->bits |= (*s->in++) << s->count; 917 | s->count += 8; 918 | } 919 | return v; 920 | } 921 | 922 | static unsigned char *emit(State *s, int len) 923 | { 924 | s->out += len; 925 | CHECK(s->out <= s->outend); 926 | return s->out-len; 927 | } 928 | 929 | static void copy(State *s, const unsigned char *src, int len) 930 | { 931 | unsigned char *dest = emit(s, len); 932 | while (len--) *dest++ = *src++; 933 | } 934 | 935 | static int build(State *s, unsigned *tree, unsigned char *lens, int symcount) 936 | { 937 | int n, codes[16], first[16], counts[16]={0}; 938 | 939 | // Frequency count. 940 | for (n=0;nbits) << 16) | 0xffff; 968 | while (lo < hi) { 969 | unsigned guess = (lo + hi) / 2; 970 | if (search < tree[guess]) hi = guess; 971 | else lo = guess + 1; 972 | } 973 | 974 | // Pull out the key and check it. 975 | key = tree[lo-1]; 976 | CHECK(((search^key) >> (32-(key&0xf))) == 0); 977 | 978 | bits(s, key & 0xf); 979 | return (key >> 4) & 0xfff; 980 | } 981 | 982 | static void run(State *s, int sym) 983 | { 984 | int length = bits(s, lenBits[sym]) + lenBase[sym]; 985 | int dsym = decode(s, s->distcodes, s->tdist); 986 | int offs = bits(s, distBits[dsym]) + distBase[dsym]; 987 | copy(s, s->out - offs, length); 988 | } 989 | 990 | static void block(State *s) 991 | { 992 | for (;;) { 993 | int sym = decode(s, s->litcodes, s->tlit); 994 | if (sym < 256) *emit(s, 1) = (unsigned char)sym; 995 | else if (sym > 256) run(s, sym-257); 996 | else break; 997 | } 998 | } 999 | 1000 | static void stored(State *s) 1001 | { 1002 | // Uncompressed data block. 1003 | int len; 1004 | bits(s, s->count & 7); 1005 | len = bits(s, 16); 1006 | CHECK(((len^s->bits)&0xffff) == 0xffff); 1007 | CHECK(s->in + len <= s->inend); 1008 | 1009 | copy(s, s->in, len); 1010 | s->in += len; 1011 | bits(s, 16); 1012 | } 1013 | 1014 | static void fixed(State *s) 1015 | { 1016 | // Fixed set of Huffman codes. 1017 | int n; 1018 | unsigned char lens[288+32]; 1019 | for (n= 0;n<=143;n++) lens[n] = 8; 1020 | for (n=144;n<=255;n++) lens[n] = 9; 1021 | for (n=256;n<=279;n++) lens[n] = 7; 1022 | for (n=280;n<=287;n++) lens[n] = 8; 1023 | for (n=0;n<32;n++) lens[288+n] = 5; 1024 | 1025 | // Build lit/dist trees. 1026 | s->tlit = build(s, s->litcodes, lens, 288); 1027 | s->tdist = build(s, s->distcodes, lens+288, 32); 1028 | } 1029 | 1030 | static void dynamic(State *s) 1031 | { 1032 | int n, i, nlit, ndist, nlen; 1033 | unsigned char lenlens[19] = {0}, lens[288+32]; 1034 | nlit = 257 + bits(s, 5); 1035 | ndist = 1 + bits(s, 5); 1036 | nlen = 4 + bits(s, 4); 1037 | for (n=0;ntlen = build(s, s->lencodes, lenlens, 19); 1042 | 1043 | // Decode code lengths. 1044 | for (n=0;nlencodes, s->tlen); 1047 | switch (sym) { 1048 | case 16: for (i = 3+bits(s,2); i; i--,n++) lens[n] = lens[n-1]; break; 1049 | case 17: for (i = 3+bits(s,3); i; i--,n++) lens[n] = 0; break; 1050 | case 18: for (i = 11+bits(s,7); i; i--,n++) lens[n] = 0; break; 1051 | default: lens[n++] = (unsigned char)sym; break; 1052 | } 1053 | } 1054 | 1055 | // Build lit/dist trees. 1056 | s->tlit = build(s, s->litcodes, lens, nlit); 1057 | s->tdist = build(s, s->distcodes, lens+nlit, ndist); 1058 | } 1059 | 1060 | int tigrInflate(void *out, unsigned outlen, const void *in, unsigned inlen) 1061 | { 1062 | int last; 1063 | State *s = (State *)calloc(1, sizeof(State)); 1064 | 1065 | // We assume we can buffer 2 extra bytes from off the end of 'in'. 1066 | s->in = (unsigned char *)in; s->inend = s->in + inlen + 2; 1067 | s->out = (unsigned char *)out; s->outend = s->out + outlen; 1068 | s->bits = 0; s->count = 0; bits(s, 0); 1069 | 1070 | if (setjmp(s->jmp) == 1) { 1071 | free(s); 1072 | return 0; 1073 | } 1074 | 1075 | do { 1076 | last = bits(s, 1); 1077 | switch (bits(s, 2)) { 1078 | case 0: stored(s); break; 1079 | case 1: fixed(s); block(s); break; 1080 | case 2: dynamic(s); block(s); break; 1081 | case 3: FAIL(); 1082 | } 1083 | } while(!last); 1084 | 1085 | free(s); 1086 | return 1; 1087 | } 1088 | 1089 | #undef CHECK 1090 | #undef FAIL 1091 | 1092 | //////// End of inlined file: tigr_inflate.c //////// 1093 | 1094 | //////// Start of inlined file: tigr_print.c //////// 1095 | 1096 | //#include "tigr_internal.h" 1097 | //////// Start of inlined file: tigr_font.h //////// 1098 | 1099 | // Auto-generated by incbin.pl from font.png 1100 | 1101 | const unsigned char tigr_font[] = { 1102 | 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, 1103 | 0x00,0x00,0x00,0xfd,0x00,0x00,0x00,0x5c,0x08,0x03,0x00,0x00,0x00,0x92,0xab,0x43, 1104 | 0x85,0x00,0x00,0x03,0x00,0x50,0x4c,0x54,0x45,0x5f,0x53,0x87,0x00,0x00,0x00,0x54, 1105 | 0x54,0x54,0xff,0xff,0xfa,0x31,0x2a,0x42,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1106 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1107 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1108 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1109 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1110 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1111 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1112 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1113 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1114 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1115 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1116 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1117 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1118 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1119 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1120 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1121 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1122 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1123 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1124 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1125 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1126 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1127 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1128 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1129 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1130 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1131 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1132 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1133 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1134 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1135 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1136 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1137 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1138 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1139 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1140 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1141 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1142 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1143 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1144 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1145 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1146 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1147 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1148 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1149 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80, 1150 | 0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00, 1151 | 0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00, 1152 | 0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x4a,0x44,0xb7,0x5a,0x00,0x00,0x0b, 1153 | 0xc5,0x49,0x44,0x41,0x54,0x78,0x9c,0xed,0x5c,0x8b,0x82,0xe4,0x26,0x0e,0x44,0xc0, 1154 | 0xff,0x7f,0xf3,0x8d,0x91,0x4a,0x2f,0xc0,0x8d,0x7b,0x7a,0x76,0x93,0x9b,0x74,0x32, 1155 | 0xc6,0xc6,0xbc,0x0a,0x09,0x50,0x21,0xbc,0xa5,0xff,0xe6,0x5f,0xe9,0x85,0x7f,0xbd, 1156 | 0x35,0xba,0xfe,0xae,0xa0,0x34,0xba,0xfe,0xff,0x7a,0xf5,0x75,0x2d,0xee,0xd1,0x52, 1157 | 0x16,0x49,0x8e,0x58,0x17,0xa4,0x5b,0x4e,0x2c,0x49,0x9b,0x14,0x28,0xcf,0x1a,0x7f, 1158 | 0xdd,0x5d,0xe5,0x8d,0x08,0x8d,0xcf,0xcf,0x2e,0xbd,0x2f,0x5d,0x5a,0x45,0xc5,0x15, 1159 | 0xfd,0x09,0xf4,0xd2,0x08,0xba,0xfe,0x1b,0x8f,0xe4,0x52,0xd6,0xce,0x89,0xa5,0x8b, 1160 | 0x10,0x2c,0xd0,0x7f,0x25,0x23,0xa0,0x20,0x2e,0x80,0x9a,0x8f,0xe7,0x36,0x8f,0x67, 1161 | 0xfa,0x2a,0x17,0xf1,0xd7,0x93,0x4f,0xe7,0xc2,0x8e,0xaa,0x2b,0xaa,0xa3,0x50,0xf4, 1162 | 0x3b,0xe8,0xe9,0xca,0x6b,0x32,0x60,0xd8,0x7d,0x5c,0xeb,0x48,0x31,0x6a,0x20,0x15, 1163 | 0xbb,0x4b,0xb4,0x43,0xdf,0x18,0x8d,0xca,0x76,0x14,0xf0,0x55,0x1a,0xa3,0xe3,0xb0, 1164 | 0x48,0xaf,0x73,0xef,0x8e,0x8a,0x0a,0x09,0x2e,0xc4,0xa7,0x50,0x14,0x90,0x9b,0xc5, 1165 | 0x0a,0x32,0x24,0x84,0x22,0xdf,0x41,0x5f,0xbe,0xf2,0x0e,0x99,0x5e,0x9d,0xc0,0xb2, 1166 | 0xbd,0x1a,0x41,0x15,0xfd,0xca,0x08,0x19,0x90,0xa0,0x1f,0xc0,0xaa,0xb6,0xa1,0x14, 1167 | 0x7f,0x3b,0xfa,0x44,0x7b,0x73,0x88,0x45,0x34,0x82,0xa5,0x86,0xbc,0x5f,0x35,0xa0, 1168 | 0xd7,0x91,0x9e,0x2c,0x1d,0xb5,0x66,0x21,0xf7,0xda,0xf8,0xbb,0xc0,0x97,0xa1,0x21, 1169 | 0xa3,0x30,0x16,0x02,0x67,0x79,0x0b,0xbd,0x48,0x0d,0x9a,0x4f,0x18,0xae,0x5c,0x4d, 1170 | 0xe3,0x10,0xe2,0x09,0xb2,0xbf,0xfa,0x8b,0xc5,0x65,0xcd,0x29,0x26,0x1d,0xd5,0x70, 1171 | 0x19,0x25,0x23,0xfe,0xab,0xc1,0xe4,0xc7,0x7b,0xd6,0x74,0xa4,0xd3,0x67,0x42,0x3c, 1172 | 0x37,0x02,0xca,0xde,0xfc,0x30,0xe0,0x2c,0x4f,0xd1,0x93,0x0d,0xa1,0xa4,0xf9,0x57, 1173 | 0xb9,0x2c,0x12,0x3c,0x7b,0xf4,0x97,0x62,0x70,0xb5,0x2b,0xcd,0x87,0xd2,0xd4,0x80, 1174 | 0xde,0xba,0xb1,0x78,0xd4,0x59,0xd3,0x91,0x4e,0x9f,0xed,0x7d,0xf7,0xca,0xce,0x9d, 1175 | 0x2d,0x1d,0xca,0x55,0x7c,0x43,0xf6,0x4e,0xf3,0x1b,0x97,0xdb,0x08,0x22,0x11,0xa1, 1176 | 0x1a,0xfa,0x52,0x04,0x09,0x1a,0xda,0x5d,0x81,0xb9,0xf5,0x3a,0xeb,0xd5,0xb2,0x46, 1177 | 0x95,0xc3,0x90,0x6e,0x1a,0xf7,0x50,0x76,0x9e,0xf3,0xa5,0x19,0x23,0xfc,0x8c,0xe6, 1178 | 0x8f,0xfe,0xf4,0x9d,0x21,0xb2,0x87,0x86,0x56,0x27,0xe9,0xb2,0x42,0x2f,0x89,0x6d, 1179 | 0x6e,0xf0,0x9a,0x2e,0xf1,0xaa,0x03,0x7e,0x7c,0xcb,0xe4,0xe2,0xf3,0x6b,0xc8,0x73, 1180 | 0xd3,0x95,0xcf,0xcd,0xf9,0xb5,0x94,0xf7,0x65,0xef,0x34,0x9f,0x27,0x3f,0x95,0xa7, 1181 | 0x3e,0x5e,0x1a,0x86,0x01,0x2d,0x33,0x4f,0x5e,0xef,0x9d,0x30,0x46,0x8a,0x2a,0x89, 1182 | 0x47,0x52,0x12,0x25,0x45,0x88,0xf8,0x2b,0x79,0x78,0xd6,0xf4,0xb5,0xf8,0xfc,0x2e, 1183 | 0x7d,0xd7,0x8e,0x0f,0x0d,0x78,0x1b,0x7d,0x2e,0x68,0xf7,0x68,0x29,0xfb,0x9c,0xa8, 1184 | 0x47,0x6d,0xe8,0xdb,0x02,0xbe,0x1b,0x76,0xed,0xf8,0x8c,0xfe,0x3d,0xcd,0xff,0x0c, 1185 | 0xfa,0xa8,0x0d,0x3f,0x88,0x3e,0x97,0x9e,0xaa,0xfa,0x3b,0xe8,0x57,0xb7,0xff,0x64, 1186 | 0xf4,0xbf,0xf9,0xc7,0xf3,0x84,0x2e,0xc3,0xd1,0xd4,0x4a,0xcf,0x79,0xca,0xb6,0xf7, 1187 | 0x58,0x88,0x5c,0x7c,0x5a,0xc0,0x8a,0x2c,0x5c,0x79,0x3d,0xbf,0xad,0x30,0xb3,0x9d, 1188 | 0xc5,0xca,0x57,0xe6,0x1b,0xdf,0x94,0x66,0x0b,0x8f,0x9a,0x44,0xa6,0x2e,0x9d,0xd7, 1189 | 0x33,0x82,0x35,0xb3,0x23,0x15,0x1a,0x82,0x84,0x78,0x32,0x82,0x2a,0x49,0x2d,0x63, 1190 | 0xd2,0x05,0x2b,0xa2,0x37,0x7b,0xff,0x65,0x45,0x3e,0x44,0xc1,0xe7,0xe8,0x9d,0xd5, 1191 | 0x58,0x6d,0xd9,0xa5,0x2e,0x76,0x9b,0xa1,0x1f,0x76,0x18,0xac,0x99,0xa9,0xf0,0x5c, 1192 | 0xa9,0xca,0xb0,0x94,0xe9,0xbd,0x19,0xb2,0x05,0x3a,0x11,0x96,0xed,0x96,0x95,0x67, 1193 | 0x55,0x61,0x92,0xa0,0xd8,0xcc,0x0d,0x56,0xd2,0x1a,0x3d,0xf9,0xaa,0x1a,0xcc,0x7d, 1194 | 0x90,0x31,0x52,0x03,0xf3,0x02,0xeb,0xd6,0xe4,0xce,0xfc,0x15,0xb6,0x9c,0x6b,0x5c, 1195 | 0x68,0xb5,0xa9,0x13,0x0c,0x6b,0x0a,0xbd,0xa0,0x96,0x76,0x42,0xdf,0x60,0xa6,0xa3, 1196 | 0x1c,0x95,0xf9,0x0b,0xf4,0x66,0xdd,0x28,0x2f,0xb8,0x43,0x5f,0x48,0xaa,0x32,0xfb, 1197 | 0xc9,0x20,0xf4,0x62,0xe8,0xaf,0xd5,0xd0,0xd6,0x64,0x41,0x5f,0xcc,0x92,0x8d,0x61, 1198 | 0x42,0x4f,0x30,0x5c,0x11,0x36,0x9f,0x7e,0x2d,0xfb,0x80,0x1e,0x5c,0x2e,0x0e,0x19, 1199 | 0xdf,0xdd,0xa1,0x40,0x92,0xf1,0xbf,0x47,0x6f,0xf4,0x49,0xe8,0x67,0x13,0xf3,0xd4, 1200 | 0x3a,0x5e,0xe8,0x80,0xda,0xd4,0x25,0x6a,0xfe,0xd0,0x9b,0xdd,0xb0,0x4a,0x95,0x26, 1201 | 0xcd,0x0f,0x20,0x16,0xe8,0xa7,0x5e,0xdc,0x69,0x3a,0xa1,0xad,0xb1,0x40,0x0a,0x4a, 1202 | 0xb5,0xd4,0x91,0xd6,0x74,0x83,0xc0,0xf7,0x93,0x31,0x0c,0x9e,0xea,0x36,0xe8,0xa9, 1203 | 0xb1,0x01,0xbb,0x56,0xac,0x5d,0x6f,0xe4,0x5e,0x49,0x8d,0x54,0x99,0x9f,0xa2,0xd7, 1204 | 0xa1,0x91,0x7b,0x65,0x85,0xde,0xf1,0x06,0xcd,0x50,0x73,0x4a,0xe5,0x47,0x5d,0xc8, 1205 | 0xd0,0x12,0x7d,0x21,0x5b,0xd1,0xe6,0x61,0x67,0x1b,0x17,0x9e,0xa4,0x44,0xf2,0x11, 1206 | 0x45,0x81,0xfd,0x8e,0x79,0x25,0x9c,0xd0,0xef,0xd8,0x4c,0x7e,0x0f,0x4c,0xfa,0x4c, 1207 | 0x5a,0x41,0xa5,0x50,0xb2,0x93,0xbd,0xad,0x78,0x45,0xb6,0xd7,0x9a,0x23,0x60,0xe3, 1208 | 0xa9,0xb3,0x65,0x6c,0xa4,0x42,0x49,0x48,0x20,0x1b,0x99,0xa4,0x44,0xf2,0x11,0xf2, 1209 | 0x09,0x59,0xe1,0x4d,0x97,0x18,0x3f,0x62,0x71,0xe3,0x59,0x8d,0x2f,0x70,0xa0,0x4e, 1210 | 0xef,0x61,0xbc,0xe7,0xe7,0x50,0xa0,0xec,0x85,0xe1,0x8d,0xf2,0xa9,0x31,0xc7,0xb9, 1211 | 0xdb,0x52,0x74,0x4b,0xc0,0x2c,0xdd,0x9f,0x31,0x48,0x57,0xe1,0x7d,0x02,0x6b,0xe3, 1212 | 0xf3,0x02,0xd7,0x29,0x7a,0xbe,0x0d,0x9a,0x7f,0xd4,0xa8,0x0f,0x86,0x3f,0x57,0xe0, 1213 | 0x7f,0xe8,0xe7,0xf0,0x16,0xfd,0x6f,0xfe,0xf9,0xbd,0x28,0x5b,0xcb,0x5c,0x17,0xf9, 1214 | 0xd7,0x62,0x3e,0xe6,0xd4,0x98,0x57,0xd3,0xe2,0xf6,0xae,0xac,0xbb,0x99,0x0d,0xb7, 1215 | 0x92,0x9e,0xb2,0xf9,0x3d,0x3f,0xff,0xf6,0x53,0xe8,0x5b,0xf3,0x93,0x92,0x2e,0x54, 1216 | 0x32,0xaf,0x26,0x83,0xe9,0x8f,0xa3,0xc7,0x6a,0x09,0x1f,0xd3,0x11,0xfa,0xb1,0x6b, 1217 | 0xbb,0xa2,0x96,0x4a,0x55,0xe3,0x6b,0xc7,0x60,0xd5,0x4e,0x70,0xb2,0x6f,0xce,0xbe, 1218 | 0x56,0xb3,0xc7,0x17,0x9b,0x9e,0x5d,0x41,0x25,0x54,0xb3,0xb4,0x89,0xd6,0x2f,0xd4, 1219 | 0xe0,0x73,0x4e,0x46,0x26,0xb3,0xaf,0xd0,0x0f,0xb3,0x61,0xe1,0x30,0x8b,0x14,0x96, 1220 | 0xd0,0xa7,0x8b,0xe8,0x80,0xde,0x0c,0x1d,0x33,0x44,0x17,0x19,0xf6,0xa1,0xaf,0xe6, 1221 | 0x0c,0xbd,0x63,0xb3,0x70,0x49,0xb5,0xc6,0xfe,0xb7,0x2b,0xfa,0xc6,0xad,0x35,0xf4, 1222 | 0x46,0x5d,0x44,0xa1,0xa2,0xe5,0xb6,0xc5,0x86,0x6c,0x38,0xf4,0x66,0x5f,0x37,0xc7, 1223 | 0xe9,0x5e,0x9a,0xd0,0xe4,0x3c,0x67,0xdb,0x6a,0xe2,0xc6,0x37,0x5e,0x98,0xbf,0xcb, 1224 | 0xcb,0xbe,0x0e,0x32,0x7b,0x6d,0x2b,0xbf,0x42,0xdf,0x26,0xd2,0x95,0xd0,0xc3,0x5d, 1225 | 0x72,0x80,0x5e,0x93,0x8c,0x6c,0x35,0x17,0xbb,0xd5,0xe9,0x4b,0x49,0x85,0xaa,0x6c, 1226 | 0xab,0x81,0x8b,0x39,0x5a,0xe6,0xa5,0x81,0xd7,0x45,0xcd,0x2f,0xb2,0x2d,0x7f,0x80, 1227 | 0x7e,0xd1,0x9c,0x29,0xfa,0x04,0xbd,0x11,0x9c,0xbb,0x81,0xba,0x61,0x39,0xea,0xe8, 1228 | 0xde,0x70,0x9a,0x69,0x20,0x21,0x7b,0x2d,0x4b,0xf4,0xaf,0xc7,0x7d,0xd4,0x7c,0xcf, 1229 | 0x6f,0xcc,0x67,0x0a,0x76,0xd1,0x8c,0x6c,0x6c,0xd1,0xeb,0x0c,0x31,0xa3,0xcf,0xb4, 1230 | 0xc9,0x17,0x84,0x66,0x08,0x89,0xf1,0xb5,0x0a,0xa7,0x99,0x36,0x70,0x62,0xab,0x39, 1231 | 0x1d,0x56,0xbc,0xfa,0x00,0x7d,0xf1,0x4e,0x16,0xe5,0x37,0x1a,0xed,0x7c,0x29,0x20, 1232 | 0x1b,0xcd,0x91,0x11,0x59,0xf1,0x7c,0x54,0xe4,0x1e,0x99,0xb5,0xb4,0x66,0xd5,0x20, 1233 | 0x17,0x9a,0x21,0x24,0x66,0xcd,0x71,0x6a,0x60,0x55,0x96,0x1d,0x2e,0x6c,0x78,0xb9, 1234 | 0xcc,0xab,0x70,0x84,0x7e,0xb3,0x82,0xae,0xa3,0x75,0x20,0x58,0xb4,0xa8,0xfc,0xc2, 1235 | 0xc2,0x5c,0x95,0xa3,0xe3,0xbc,0x64,0x6b,0xea,0x75,0xed,0xa9,0x18,0xcb,0x6e,0x06, 1236 | 0x6d,0x4f,0xd9,0x3e,0x8b,0xde,0x3b,0x1f,0x51,0x21,0x4d,0x51,0x3f,0x8f,0x3e,0xb6, 1237 | 0xe3,0x4f,0xa1,0xf7,0xce,0x47,0xad,0x70,0x8a,0xba,0x41,0xaf,0x9a,0x5e,0x56,0x8c, 1238 | 0xf6,0x18,0x7d,0x6c,0xc7,0x7b,0xe8,0x7f,0xf3,0x2f,0xf4,0x91,0x84,0x79,0x2c,0x15, 1239 | 0x8b,0xf7,0x61,0x93,0xcd,0x62,0x31,0x28,0xcd,0x4f,0x20,0x16,0x2c,0x17,0x63,0x2f, 1240 | 0x3a,0xec,0x80,0xb0,0x36,0x16,0x9b,0x31,0x72,0xa5,0xbd,0x60,0xe1,0x6d,0x84,0x54, 1241 | 0xbe,0x16,0xff,0xeb,0x2e,0x8f,0x6b,0xd8,0x01,0x7a,0x31,0xb2,0xad,0xe9,0xf9,0x4c, 1242 | 0xc2,0x12,0xbd,0x98,0x91,0x7c,0x92,0xa9,0xe8,0x49,0x87,0xa2,0x53,0x20,0xeb,0xb4, 1243 | 0xbd,0x78,0x8e,0xde,0xce,0x8c,0x55,0x1c,0x9a,0x71,0xb5,0x6c,0xd0,0x4b,0x0a,0x39, 1244 | 0x73,0xf0,0x12,0xfd,0x30,0xf2,0x29,0xa0,0x9f,0x1a,0x52,0x66,0x2f,0xde,0x38,0xc8, 1245 | 0x20,0x87,0xd9,0x44,0xde,0x14,0xd0,0x17,0x00,0x56,0x2d,0xf0,0x47,0x3e,0x20,0x4d, 1246 | 0x2c,0xe2,0x2b,0xf4,0x62,0xab,0xb2,0x1c,0xaf,0x1e,0x08,0xb5,0xb0,0xc9,0x67,0x54, 1247 | 0xeb,0xfa,0x4d,0x14,0xe9,0x15,0xfa,0x91,0x7a,0x78,0xbb,0xba,0xa2,0x5c,0x69,0x3e, 1248 | 0x9c,0x69,0xea,0xad,0xe3,0x9e,0xae,0xc6,0xe8,0x71,0xc2,0xa9,0x4b,0xa3,0xc6,0x41, 1249 | 0x47,0xe5,0x20,0xe3,0x7f,0x77,0xe4,0x83,0x57,0xef,0x8e,0x45,0xdc,0x99,0x2d,0x6e, 1250 | 0xdc,0x14,0x27,0xc7,0x02,0x82,0x45,0x1d,0xb6,0x20,0x57,0x23,0xe5,0x0f,0x21,0x3a, 1251 | 0x07,0xde,0x11,0x7a,0x12,0xcf,0x0c,0x00,0xa9,0x28,0x5e,0x68,0x3e,0x22,0x65,0xfe, 1252 | 0x56,0x86,0x42,0x15,0x9a,0x5f,0xa5,0xd0,0x26,0x64,0x43,0xbb,0x2c,0x68,0xaa,0xaa, 1253 | 0x96,0x9a,0x2d,0xa4,0x9b,0xc3,0xb3,0x08,0x44,0x02,0x66,0xe6,0xab,0x4a,0x5e,0x42, 1254 | 0xf8,0x7a,0x99,0x8e,0xf8,0x1d,0xc9,0xde,0xcf,0x7a,0x1b,0xcd,0xcf,0x5b,0xe5,0x8a, 1255 | 0x5e,0xa6,0x19,0x31,0xf0,0x71,0xa8,0x53,0x35,0x1f,0x1c,0xc4,0xcf,0xab,0xeb,0x55, 1256 | 0x70,0x9e,0xf5,0xe6,0x46,0xc8,0x64,0x61,0x4e,0xd7,0x06,0x87,0x32,0xd0,0x4b,0x75, 1257 | 0x62,0x1e,0x1f,0xc9,0x1e,0xd3,0x33,0x38,0x86,0x4c,0x46,0x18,0x9d,0x0d,0x3c,0xd2, 1258 | 0x8d,0x00,0xe9,0x2a,0xa3,0xd1,0x62,0xe0,0xd3,0x34,0xee,0xe5,0x28,0xd9,0x3d,0xfa, 1259 | 0x05,0xda,0xd5,0xe4,0x03,0x1e,0xa4,0xe6,0x3e,0x54,0x1c,0xe8,0xb5,0x3a,0x6e,0xed, 1260 | 0x2b,0xf4,0x45,0xcd,0xb4,0x33,0xf4,0x51,0xf3,0xc5,0xff,0x5b,0x1d,0x43,0x01,0xfd, 1261 | 0x90,0xb9,0x2e,0xcc,0x7a,0xdf,0x45,0x2f,0xb5,0x38,0x7f,0x2d,0x18,0x53,0x98,0x94, 1262 | 0xe9,0x1c,0xbd,0x9a,0x69,0xbd,0xe1,0xd0,0xac,0xda,0xed,0x95,0x27,0x25,0xef,0x26, 1263 | 0xb1,0xb0,0x60,0x42,0xd2,0xe3,0x6c,0xe3,0x2d,0xd8,0x91,0xac,0x78,0x76,0xf7,0x01, 1264 | 0xf4,0x52,0x8b,0xf3,0xec,0x04,0x62,0xa6,0x44,0xad,0x9f,0x9d,0xdd,0xf2,0xd5,0x77, 1265 | 0x73,0x2b,0x75,0x85,0x9c,0x9a,0x12,0xc3,0x9e,0x5f,0xcf,0xb1,0xdd,0xdf,0x7d,0x1f, 1266 | 0xfd,0xaa,0x96,0x45,0x79,0xfa,0xf8,0x00,0xbd,0xe5,0xdb,0x56,0xfc,0x1f,0xfa,0xff, 1267 | 0x27,0xf4,0xbf,0xf9,0x97,0x3b,0x5f,0xa7,0x75,0x5b,0xf4,0xc3,0x46,0x9e,0xbe,0xc3, 1268 | 0xd9,0x75,0x18,0x01,0xf2,0x31,0x01,0xe9,0x97,0x32,0x7a,0xd5,0x24,0x32,0x1b,0x7b, 1269 | 0x7b,0xa2,0xe9,0x26,0x60,0x24,0xfa,0x66,0x53,0xb8,0x38,0x21,0x2f,0xdd,0x33,0xab, 1270 | 0xa2,0x67,0xe0,0xa7,0xbb,0x03,0xf1,0xaf,0xd1,0xc3,0x06,0xe7,0x35,0x34,0xa2,0x17, 1271 | 0x5a,0xc0,0xe7,0x95,0xf9,0x61,0x18,0x01,0xf2,0x7d,0x40,0x3a,0x3d,0xec,0x93,0xc0, 1272 | 0x2c,0x71,0x1f,0xb2,0x10,0x3e,0x6c,0x88,0xe8,0x9b,0xfb,0x12,0xc0,0x8b,0xa0,0x83, 1273 | 0xf5,0x50,0x42,0x4a,0x24,0x5d,0xe2,0xee,0x1e,0xa1,0x6f,0x8e,0x44,0x34,0x66,0x68, 1274 | 0xf3,0xe1,0x0c,0x66,0x75,0x38,0xf6,0xc4,0xa7,0xdd,0xe4,0xb8,0x17,0xdb,0x20,0x4a, 1275 | 0xc1,0x70,0x65,0xa3,0xd7,0x23,0x13,0xf5,0x00,0x4d,0x36,0xeb,0xb8,0xde,0xa2,0x27, 1276 | 0xd3,0x38,0x81,0xdf,0x19,0x68,0x95,0x72,0xa6,0xbb,0xd7,0x4b,0x7e,0x44,0x0f,0xc3, 1277 | 0xa6,0x81,0x58,0xeb,0x0e,0x2d,0x7a,0xa1,0x13,0x3e,0xce,0x11,0x68,0x6c,0x28,0x8a, 1278 | 0xb9,0x57,0xe1,0xcb,0x80,0xdd,0x01,0x0e,0xec,0xd0,0x63,0x5f,0x56,0xde,0xb2,0x55, 1279 | 0xca,0x3b,0x9b,0x69,0xeb,0x5e,0x77,0x8b,0x4d,0xf3,0xa5,0x24,0x92,0x0e,0xd5,0x31, 1280 | 0x70,0x95,0x63,0x4c,0x1b,0x76,0xe6,0x03,0xf4,0xf0,0x51,0x69,0xd3,0x66,0x5f,0x0e, 1281 | 0x89,0x31,0x67,0x54,0x96,0x58,0xf7,0x58,0xf6,0xaa,0x91,0x32,0x36,0x44,0xd1,0x5b, 1282 | 0x13,0x4b,0x5a,0x50,0x88,0x55,0x21,0xde,0x2a,0xbf,0x01,0x1d,0xf6,0xaa,0x33,0x7a, 1283 | 0x54,0x51,0x6d,0x84,0xb1,0xb1,0x87,0xa3,0x86,0xda,0x61,0xda,0xdd,0x2f,0x0d,0x9e, 1284 | 0x49,0xf6,0x10,0x8f,0x17,0x85,0x3b,0x29,0xca,0x1d,0xaf,0x6c,0x58,0x4e,0xbb,0xc9, 1285 | 0xa1,0x18,0x68,0x24,0xcf,0x16,0xaa,0xf9,0x51,0xf6,0xb8,0xa2,0x9f,0xdc,0x06,0xb4, 1286 | 0xed,0xd2,0xca,0xf9,0x49,0xbf,0xe9,0x27,0x55,0x54,0x53,0x6e,0xee,0x65,0xd4,0xaf, 1287 | 0x77,0xba,0x13,0xf2,0x4d,0xcd,0x8f,0x5e,0x14,0x8c,0xfb,0x42,0x2a,0x2b,0xe2,0x29, 1288 | 0x4d,0x99,0x2b,0xe9,0x10,0xd7,0xb9,0x8f,0x1a,0xf6,0x28,0x12,0x7a,0x67,0x2e,0xeb, 1289 | 0x45,0xd1,0x33,0x77,0x8c,0x73,0xbe,0xa5,0x72,0x23,0x9b,0x8f,0xd9,0xee,0xee,0x9e, 1290 | 0xa1,0xa7,0x84,0x96,0xa0,0xc3,0x26,0x7b,0x1c,0x47,0x13,0x2d,0x0e,0x4f,0x45,0xbe, 1291 | 0xe7,0x0b,0x30,0x7d,0x12,0x8b,0x8e,0x34,0xd9,0xd0,0x83,0x71,0xb5,0x1b,0xf4,0xb2, 1292 | 0xd9,0x21,0x0a,0x64,0x4b,0x48,0xbe,0x6b,0xed,0x99,0xe6,0xcb,0xa6,0x98,0x9e,0xf7, 1293 | 0xaa,0x90,0x22,0xc1,0x33,0xab,0xc7,0xd1,0x20,0x41,0x8a,0x32,0x0c,0xe2,0x9d,0x92, 1294 | 0x58,0xb4,0xdb,0xbf,0x88,0xb2,0x37,0xf7,0x4d,0xd8,0xee,0x5a,0x98,0x89,0x50,0x20, 1295 | 0x1e,0x65,0xf3,0x5d,0x93,0x3d,0xb7,0x73,0xf4,0x71,0xa5,0xcc,0x61,0xde,0x7b,0x8f, 1296 | 0x78,0x77,0xe8,0x57,0x85,0xf5,0x9c,0xcf,0x34,0x3f,0xa5,0xed,0x76,0x0a,0x61,0x5d, 1297 | 0x8f,0x3b,0x76,0x1c,0xef,0x30,0x8c,0x1e,0xa0,0x9f,0xc5,0x34,0x35,0xe6,0x07,0xd1, 1298 | 0x7b,0x37,0x87,0x56,0xd8,0x5e,0xa1,0xdf,0xdc,0x1d,0x6c,0x6e,0xad,0x04,0xfb,0xf7, 1299 | 0xd0,0x2f,0x2b,0x24,0x0c,0xf2,0x87,0xe8,0xe1,0xea,0xb9,0x45,0xff,0x9b,0x7f,0x61, 1300 | 0x5c,0x61,0xd0,0xc7,0x1d,0x2c,0xf1,0x8a,0xf8,0x1d,0x2d,0xed,0xde,0x06,0x3a,0xe4, 1301 | 0xb2,0x76,0xb3,0x62,0x41,0x4c,0x24,0x4e,0xae,0x62,0xeb,0x36,0xcb,0x6c,0x49,0x8e, 1302 | 0x1a,0x32,0x89,0x7b,0x93,0xd1,0x32,0x28,0xcf,0xa2,0xd8,0xee,0x3e,0x56,0x69,0x29, 1303 | 0x03,0x8b,0x28,0xe7,0x81,0xdb,0x1c,0x1f,0x66,0xce,0xe8,0x61,0x0a,0x17,0xf9,0x00, 1304 | 0x40,0xbe,0x04,0xc6,0x2e,0x6f,0x85,0x1d,0x5b,0xba,0x7b,0xd3,0x65,0x59,0x74,0x99, 1305 | 0xb1,0x65,0x76,0xd8,0x90,0x09,0xfd,0x26,0xa3,0xb5,0x9c,0xdc,0x37,0xe4,0xc5,0x4e, 1306 | 0x69,0xb9,0x7e,0x38,0x0d,0x23,0xfa,0x62,0xa6,0xf1,0x3e,0x58,0x5d,0x8b,0x33,0xea, 1307 | 0xb1,0x9d,0x7a,0xd4,0x00,0x31,0xae,0x8b,0xf8,0x72,0xc2,0x49,0x96,0x9a,0xc2,0xe2, 1308 | 0x9f,0xa1,0x6d,0x52,0xc4,0x40,0xef,0xf2,0x1e,0x85,0xea,0x41,0x50,0xcb,0x18,0xeb, 1309 | 0xf4,0x3a,0x60,0x56,0x33,0x5f,0xb9,0xed,0xc2,0x11,0x9e,0x34,0x40,0x36,0x91,0xa1, 1310 | 0x3b,0xeb,0xa3,0x6d,0xab,0x10,0x39,0x78,0xec,0x8d,0x32,0x8e,0xf3,0xba,0x90,0x7d, 1311 | 0x94,0x14,0xbf,0xb6,0xa2,0x4d,0x50,0x36,0x57,0x32,0x11,0x59,0x57,0x1e,0x81,0x20, 1312 | 0x3d,0xa2,0x95,0xc9,0xd8,0x3a,0x0c,0xc7,0x8e,0x80,0x9e,0x20,0x7b,0xa7,0xa8,0xc7, 1313 | 0xa1,0x68,0x90,0x93,0x7d,0xdd,0x04,0x3b,0xf4,0x5e,0xf3,0x9f,0xa1,0xc7,0x6c,0xda, 1314 | 0x16,0x5f,0xc0,0x6c,0xba,0x0b,0xfd,0x25,0x56,0xf7,0xfb,0x9a,0x0f,0xd9,0xfb,0x7f, 1315 | 0x56,0x80,0x7b,0x61,0x1d,0x9c,0xa0,0x0f,0x8e,0xc7,0x97,0xa1,0x7c,0x53,0x27,0xba, 1316 | 0x73,0x80,0x5e,0x17,0x8d,0x26,0x56,0x77,0x40,0xff,0x54,0xf3,0x0b,0xf4,0x20,0x1e, 1317 | 0x41,0x7b,0x3c,0xeb,0x21,0x73,0x54,0xd0,0x97,0xa1,0xb8,0x73,0x09,0x92,0x7c,0x95, 1318 | 0xc1,0x7b,0x20,0x71,0x30,0x50,0xde,0xf8,0x73,0x67,0x0f,0x42,0x3d,0xca,0x66,0xb4, 1319 | 0xa8,0xee,0x83,0xd5,0x75,0xfa,0x1c,0x68,0xfa,0x37,0x06,0x76,0x21,0xaf,0x37,0xec, 1320 | 0xc6,0x09,0x9f,0xeb,0x6c,0x33,0xc0,0x43,0x03,0x4b,0x7a,0xe4,0xad,0x25,0xee,0x6a, 1321 | 0x3e,0x09,0x61,0x67,0x38,0x1b,0xe0,0x26,0xd8,0x5d,0xdf,0xa8,0x78,0xb5,0xde,0x3f, 1322 | 0x6c,0xb9,0xc5,0x7c,0x13,0xbd,0x67,0xaf,0xbf,0x0f,0xfd,0xa2,0x41,0xff,0x2a,0xf4, 1323 | 0xbf,0xf9,0x57,0xf6,0x1c,0xc1,0x91,0x8f,0xb6,0x63,0x39,0x1a,0x6e,0xca,0x68,0x04, 1324 | 0x5e,0xd3,0x99,0xd9,0xf8,0xe3,0x66,0x0d,0xdc,0x00,0xe1,0x6d,0x33,0xda,0x87,0x38, 1325 | 0x8e,0x3f,0xcc,0x36,0xc6,0xae,0xa2,0x5c,0x92,0x0b,0xb0,0x95,0x5b,0xf4,0xf7,0x3c, 1326 | 0x43,0x76,0xfb,0xed,0x94,0xdb,0x68,0x53,0x53,0xdb,0xa5,0x1e,0x34,0xc3,0x51,0x96, 1327 | 0x84,0xfe,0x19,0xc7,0xf1,0x87,0xd9,0xca,0x73,0x96,0x63,0xc6,0xde,0x79,0x1e,0x5e, 1328 | 0xa6,0xe5,0x94,0x9b,0xec,0xd8,0xc9,0xf1,0xa8,0xd1,0xb7,0x0f,0x8a,0xc2,0xf6,0xd3, 1329 | 0x78,0x7e,0x83,0xe3,0xe8,0x8b,0x2e,0x5f,0xa0,0xeb,0x91,0xaf,0x07,0xe1,0x53,0xfb, 1330 | 0x10,0x7f,0xe6,0xb4,0x44,0x5b,0xb4,0x4d,0x87,0x45,0xc1,0x56,0x7a,0x93,0xe3,0x44, 1331 | 0xf4,0xe4,0xed,0xb6,0xe3,0x10,0x56,0x9b,0x63,0xb8,0xb7,0xdc,0x20,0xa3,0x6f,0x4a, 1332 | 0xb7,0x1e,0xda,0xb9,0xb0,0xb3,0xda,0x29,0xc7,0xf1,0xe8,0x43,0x44,0xef,0xdf,0xb0, 1333 | 0xf3,0x23,0xc7,0xd3,0xe7,0x5d,0x38,0xa3,0x2f,0x8f,0x1a,0xaf,0x20,0xd4,0xcd,0x7c, 1334 | 0xc8,0x71,0x5e,0xa2,0x7f,0xc3,0xce,0x7f,0x9c,0xe7,0x73,0xe8,0x31,0x4d,0x1c,0x72, 1335 | 0x9c,0x7b,0xf4,0x6f,0x6b,0xfe,0x09,0x21,0x91,0x70,0x46,0x4f,0x79,0x0e,0x3a,0x2d, 1336 | 0x4a,0x66,0xfa,0x73,0x8e,0x83,0x76,0x2b,0xab,0x42,0x04,0xcf,0xf9,0xc6,0x58,0x1e, 1337 | 0x84,0xca,0x23,0xda,0x09,0x31,0x11,0x76,0x64,0x87,0xc2,0xfe,0x24,0xc7,0x21,0xfb, 1338 | 0xd2,0x2b,0x44,0xf4,0xfe,0x51,0x96,0x73,0x1b,0xe2,0xcf,0x5d,0xde,0xa9,0xf6,0xb9, 1339 | 0x9d,0x1b,0x34,0xdf,0x47,0x7c,0x00,0xfd,0xc2,0x47,0xf7,0x0f,0x43,0xef,0xf7,0x20, 1340 | 0x82,0x67,0xfa,0x03,0xe8,0x8f,0xc3,0xbf,0x86,0xde,0xff,0xeb,0x21,0xe1,0xdf,0x3a, 1341 | 0xec,0xbf,0xdd,0x97,0xf3,0x3f,0xca,0x1b,0xaa,0xdf,0xfc,0xa4,0x05,0x0a,0x00,0x00, 1342 | 0x00,0x00,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 }; 1343 | 1344 | int tigr_font_size = (int)sizeof(tigr_font); 1345 | 1346 | 1347 | //////// End of inlined file: tigr_font.h //////// 1348 | 1349 | #include 1350 | #include 1351 | #include 1352 | #include 1353 | 1354 | #ifdef _MSC_VER 1355 | #define vsnprintf _vsnprintf 1356 | #endif 1357 | 1358 | TigrFont tigrStockFont; 1359 | TigrFont *tfont = &tigrStockFont; 1360 | 1361 | // Converts 8-bit codepage entries into Unicode code points. 1362 | static int cp1252[] = { 1363 | 0x20ac,0xfffd,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,0x02c6,0x2030,0x0160,0x2039,0x0152,0xfffd,0x017d,0xfffd, 1364 | 0xfffd,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,0x02dc,0x2122,0x0161,0x203a,0x0153,0xfffd,0x017e,0x0178, 1365 | 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af, 1366 | 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf, 1367 | 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf, 1368 | 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df, 1369 | 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef, 1370 | 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff, 1371 | }; 1372 | static int border(Tigr *bmp, int x, int y) 1373 | { 1374 | TPixel top = tigrGet(bmp, 0, 0); 1375 | TPixel c = tigrGet(bmp, x, y); 1376 | return (c.r == top.r && c.g == top.g && c.b == top.b) || x >= bmp->w || y >= bmp->h; 1377 | } 1378 | 1379 | static void scan(Tigr *bmp, int *x, int *y, int *rowh) 1380 | { 1381 | while (*y < bmp->h) 1382 | { 1383 | if (*x >= bmp->w) { 1384 | *x = 0; 1385 | (*y) += *rowh; 1386 | *rowh = 1; 1387 | } 1388 | if (!border(bmp, *x, *y)) 1389 | return; 1390 | (*x)++; 1391 | } 1392 | } 1393 | 1394 | int tigrLoadGlyphs(TigrFont *font, int codepage) 1395 | { 1396 | int i, x=0, y=0, w, h, rowh=1; 1397 | TigrGlyph *g; 1398 | switch (codepage) { 1399 | case 0: font->numGlyphs = 128-32; break; 1400 | case 1252: font->numGlyphs = 256-32; break; 1401 | } 1402 | 1403 | font->glyphs = (TigrGlyph *)calloc(font->numGlyphs, sizeof(TigrGlyph)); 1404 | for (i=32;inumGlyphs+32;i++) 1405 | { 1406 | // Find the next glyph. 1407 | scan(font->bitmap, &x, &y, &rowh); 1408 | if (y >= font->bitmap->h) 1409 | { 1410 | errno = EINVAL; 1411 | return 0; 1412 | } 1413 | 1414 | // Scan the width and height 1415 | w = h = 0; 1416 | while (!border(font->bitmap, x+w, y)) w++; 1417 | while (!border(font->bitmap, x, y+h)) h++; 1418 | 1419 | // Look up the Unicode code point. 1420 | g = &font->glyphs[i-32]; 1421 | if (i < 128) g->code = i; // ASCII 1422 | else if (codepage == 1252) g->code = cp1252[i-128]; 1423 | else { errno = EINVAL; return 0; } 1424 | 1425 | g->x = x; g->y = y; g->w = w; g->h = h; 1426 | x += w; 1427 | if (h != font->glyphs[0].h) { errno = EINVAL; return 0; } 1428 | 1429 | if (h > rowh) 1430 | rowh = h; 1431 | } 1432 | 1433 | // Sort by code point. 1434 | for (i=1;inumGlyphs;i++) 1435 | { 1436 | int j = i; 1437 | TigrGlyph g = font->glyphs[i]; 1438 | while (j > 0 && font->glyphs[j-1].code > g.code) { 1439 | font->glyphs[j] = font->glyphs[j-1]; 1440 | j--; 1441 | } 1442 | font->glyphs[j] = g; 1443 | } 1444 | 1445 | return 1; 1446 | } 1447 | 1448 | TigrFont *tigrLoadFont(Tigr *bitmap, int codepage) 1449 | { 1450 | TigrFont *font = (TigrFont *)calloc(1, sizeof(TigrFont)); 1451 | font->bitmap = bitmap; 1452 | if (!tigrLoadGlyphs(font, codepage)) 1453 | { 1454 | tigrFreeFont(font); 1455 | return NULL; 1456 | } 1457 | return font; 1458 | } 1459 | 1460 | void tigrFreeFont(TigrFont *font) 1461 | { 1462 | tigrFree(font->bitmap); 1463 | free(font->glyphs); 1464 | free(font); 1465 | } 1466 | 1467 | static TigrGlyph *get(TigrFont *font, int code) 1468 | { 1469 | unsigned lo = 0, hi = font->numGlyphs; 1470 | while (lo < hi) { 1471 | unsigned guess = (lo + hi) / 2; 1472 | if (code < font->glyphs[guess].code) hi = guess; 1473 | else lo = guess + 1; 1474 | } 1475 | 1476 | if (lo == 0 || font->glyphs[lo-1].code != code) 1477 | return &font->glyphs['?' - 32]; 1478 | else 1479 | return &font->glyphs[lo-1]; 1480 | } 1481 | 1482 | void tigrSetupFont(TigrFont *font) 1483 | { 1484 | // Load the stock font if needed. 1485 | if (font == tfont && !tfont->bitmap) 1486 | { 1487 | tfont->bitmap = tigrLoadImageMem(tigr_font, tigr_font_size); 1488 | tigrLoadGlyphs(tfont, 1252); 1489 | } 1490 | } 1491 | 1492 | void tigrPrint(Tigr *dest, TigrFont *font, int x, int y, TPixel color, const char *text, ...) 1493 | { 1494 | char tmp[1024]; 1495 | TigrGlyph *g; 1496 | va_list args; 1497 | const char *p; 1498 | int start = x, c; 1499 | 1500 | tigrSetupFont(font); 1501 | 1502 | // Expand the formatting string. 1503 | va_start(args, text); 1504 | vsnprintf(tmp, sizeof(tmp), text, args); 1505 | tmp[sizeof(tmp)-1] = 0; 1506 | va_end(args); 1507 | 1508 | // Print each glyph. 1509 | p = tmp; 1510 | while (*p) 1511 | { 1512 | p = tigrDecodeUTF8(p, &c); 1513 | if (c == '\r') 1514 | continue; 1515 | if (c == '\n') { 1516 | x = start; 1517 | y += tigrTextHeight(font, ""); 1518 | continue; 1519 | } 1520 | g = get(font, c); 1521 | tigrBlitTint(dest, font->bitmap, x, y, g->x, g->y, g->w, g->h, color); 1522 | x += g->w; 1523 | } 1524 | } 1525 | 1526 | int tigrTextWidth(TigrFont *font, const char *text) 1527 | { 1528 | int x = 0, w = 0, c; 1529 | tigrSetupFont(font); 1530 | 1531 | while (*text) 1532 | { 1533 | text = tigrDecodeUTF8(text, &c); 1534 | if (c == '\n' || c == '\r') { 1535 | x = 0; 1536 | } else { 1537 | x += get(font, c)->w; 1538 | w = (x > w) ? x : w; 1539 | } 1540 | } 1541 | return w; 1542 | } 1543 | 1544 | int tigrTextHeight(TigrFont *font, const char *text) 1545 | { 1546 | int rowh, h, c; 1547 | tigrSetupFont(font); 1548 | 1549 | h = rowh = get(font, 0)->h; 1550 | while (*text) 1551 | { 1552 | text = tigrDecodeUTF8(text, &c); 1553 | if (c == '\n' && *text) 1554 | h += rowh; 1555 | } 1556 | return h; 1557 | } 1558 | 1559 | //////// End of inlined file: tigr_print.c //////// 1560 | 1561 | 1562 | #ifdef _WIN32 1563 | #define WIN32_LEAN_AND_MEAN 1564 | #include 1565 | //////// Start of inlined file: tigr_upscale_d3d9_vs.h //////// 1566 | 1567 | #if 0 1568 | // 1569 | // Generated by Microsoft (R) HLSL Shader Compiler 10.0.10011.16384 1570 | // 1571 | // Parameters: 1572 | // 1573 | // float4 screenSize; 1574 | // float4 texSize; 1575 | // 1576 | // 1577 | // Registers: 1578 | // 1579 | // Name Reg Size 1580 | // ------------ ----- ---- 1581 | // screenSize c0 1 1582 | // texSize c1 1 1583 | // 1584 | 1585 | vs_2_0 1586 | def c2, -0.5, 2, -1, 0 1587 | def c3, 1, -1, 0, 0 1588 | dcl_position v0 1589 | dcl_texcoord v1 1590 | mul oT0.xy, v1, c1 1591 | add r0.xyz, v0.xyxw, c2.x 1592 | rcp r1.xw, c0.x 1593 | rcp r1.y, c0.y 1594 | mul r0.xyz, r0, r1.xyww 1595 | mad r0.xyz, r0, c2.y, c2.z 1596 | mad oPos.xyw, r0.xyzz, c3.xyzz, c3.zzzx 1597 | mov oPos.z, v0.z 1598 | 1599 | // approximately 8 instruction slots used 1600 | #endif 1601 | 1602 | const BYTE tigrUpscaleVSCode[] = 1603 | { 1604 | 0, 2, 254, 255, 254, 255, 1605 | 42, 0, 67, 84, 65, 66, 1606 | 28, 0, 0, 0, 111, 0, 1607 | 0, 0, 0, 2, 254, 255, 1608 | 2, 0, 0, 0, 28, 0, 1609 | 0, 0, 8, 131, 0, 0, 1610 | 104, 0, 0, 0, 68, 0, 1611 | 0, 0, 2, 0, 0, 0, 1612 | 1, 0, 2, 0, 80, 0, 1613 | 0, 0, 0, 0, 0, 0, 1614 | 96, 0, 0, 0, 2, 0, 1615 | 1, 0, 1, 0, 6, 0, 1616 | 80, 0, 0, 0, 0, 0, 1617 | 0, 0, 115, 99, 114, 101, 1618 | 101, 110, 83, 105, 122, 101, 1619 | 0, 171, 1, 0, 3, 0, 1620 | 1, 0, 4, 0, 1, 0, 1621 | 0, 0, 0, 0, 0, 0, 1622 | 116, 101, 120, 83, 105, 122, 1623 | 101, 0, 118, 115, 95, 50, 1624 | 95, 48, 0, 77, 105, 99, 1625 | 114, 111, 115, 111, 102, 116, 1626 | 32, 40, 82, 41, 32, 72, 1627 | 76, 83, 76, 32, 83, 104, 1628 | 97, 100, 101, 114, 32, 67, 1629 | 111, 109, 112, 105, 108, 101, 1630 | 114, 32, 49, 48, 46, 48, 1631 | 46, 49, 48, 48, 49, 49, 1632 | 46, 49, 54, 51, 56, 52, 1633 | 0, 171, 81, 0, 0, 5, 1634 | 2, 0, 15, 160, 0, 0, 1635 | 0, 191, 0, 0, 0, 64, 1636 | 0, 0, 128, 191, 0, 0, 1637 | 0, 0, 81, 0, 0, 5, 1638 | 3, 0, 15, 160, 0, 0, 1639 | 128, 63, 0, 0, 128, 191, 1640 | 0, 0, 0, 0, 0, 0, 1641 | 0, 0, 31, 0, 0, 2, 1642 | 0, 0, 0, 128, 0, 0, 1643 | 15, 144, 31, 0, 0, 2, 1644 | 5, 0, 0, 128, 1, 0, 1645 | 15, 144, 5, 0, 0, 3, 1646 | 0, 0, 3, 224, 1, 0, 1647 | 228, 144, 1, 0, 228, 160, 1648 | 2, 0, 0, 3, 0, 0, 1649 | 7, 128, 0, 0, 196, 144, 1650 | 2, 0, 0, 160, 6, 0, 1651 | 0, 2, 1, 0, 9, 128, 1652 | 0, 0, 0, 160, 6, 0, 1653 | 0, 2, 1, 0, 2, 128, 1654 | 0, 0, 85, 160, 5, 0, 1655 | 0, 3, 0, 0, 7, 128, 1656 | 0, 0, 228, 128, 1, 0, 1657 | 244, 128, 4, 0, 0, 4, 1658 | 0, 0, 7, 128, 0, 0, 1659 | 228, 128, 2, 0, 85, 160, 1660 | 2, 0, 170, 160, 4, 0, 1661 | 0, 4, 0, 0, 11, 192, 1662 | 0, 0, 164, 128, 3, 0, 1663 | 164, 160, 3, 0, 42, 160, 1664 | 1, 0, 0, 2, 0, 0, 1665 | 4, 192, 0, 0, 170, 144, 1666 | 255, 255, 0, 0 1667 | }; 1668 | 1669 | //////// End of inlined file: tigr_upscale_d3d9_vs.h //////// 1670 | 1671 | //////// Start of inlined file: tigr_upscale_d3d9_ps.h //////// 1672 | 1673 | #if 0 1674 | // 1675 | // Generated by Microsoft (R) HLSL Shader Compiler 10.0.10011.16384 1676 | // 1677 | // Parameters: 1678 | // 1679 | // sampler2D image; 1680 | // float4 params; 1681 | // float4 texSize; 1682 | // 1683 | // 1684 | // Registers: 1685 | // 1686 | // Name Reg Size 1687 | // ------------ ----- ---- 1688 | // texSize c1 1 1689 | // params c2 1 1690 | // image s0 1 1691 | // 1692 | 1693 | ps_2_0 1694 | def c0, 0.5, -0.5, 0, 0 1695 | dcl t0.xy 1696 | dcl_2d s0 1697 | frc r0.w, t0.y 1698 | add r0.x, -r0.w, c0.x 1699 | mov r1.w, c0.x 1700 | mad r0.x, c2.z, r0.x, r1.w 1701 | add r0.x, r0.x, r0.x 1702 | frc r0.yz, t0.zxyw 1703 | add r0.yz, -r0, t0.zxyw 1704 | add r0.yz, r0, c0.x 1705 | lrp r1.xy, c2, t0, r0.yzxw 1706 | mul r2.x, r1.x, c1.z 1707 | mul r2.y, r1.y, c1.w 1708 | texld r2, r2, s0 1709 | mad r0.xyz, r2, r0.x, c0.y 1710 | mad r2.xyz, c2.w, r0, r1.w 1711 | mov oC0, r2 1712 | 1713 | // approximately 15 instruction slots used (1 texture, 14 arithmetic) 1714 | #endif 1715 | 1716 | const BYTE tigrUpscalePSCode[] = 1717 | { 1718 | 0, 2, 255, 255, 254, 255, 1719 | 52, 0, 67, 84, 65, 66, 1720 | 28, 0, 0, 0, 151, 0, 1721 | 0, 0, 0, 2, 255, 255, 1722 | 3, 0, 0, 0, 28, 0, 1723 | 0, 0, 8, 131, 0, 0, 1724 | 144, 0, 0, 0, 88, 0, 1725 | 0, 0, 3, 0, 0, 0, 1726 | 1, 0, 0, 0, 96, 0, 1727 | 0, 0, 0, 0, 0, 0, 1728 | 112, 0, 0, 0, 2, 0, 1729 | 2, 0, 1, 0, 10, 0, 1730 | 120, 0, 0, 0, 0, 0, 1731 | 0, 0, 136, 0, 0, 0, 1732 | 2, 0, 1, 0, 1, 0, 1733 | 6, 0, 120, 0, 0, 0, 1734 | 0, 0, 0, 0, 105, 109, 1735 | 97, 103, 101, 0, 171, 171, 1736 | 4, 0, 12, 0, 1, 0, 1737 | 1, 0, 1, 0, 0, 0, 1738 | 0, 0, 0, 0, 112, 97, 1739 | 114, 97, 109, 115, 0, 171, 1740 | 1, 0, 3, 0, 1, 0, 1741 | 4, 0, 1, 0, 0, 0, 1742 | 0, 0, 0, 0, 116, 101, 1743 | 120, 83, 105, 122, 101, 0, 1744 | 112, 115, 95, 50, 95, 48, 1745 | 0, 77, 105, 99, 114, 111, 1746 | 115, 111, 102, 116, 32, 40, 1747 | 82, 41, 32, 72, 76, 83, 1748 | 76, 32, 83, 104, 97, 100, 1749 | 101, 114, 32, 67, 111, 109, 1750 | 112, 105, 108, 101, 114, 32, 1751 | 49, 48, 46, 48, 46, 49, 1752 | 48, 48, 49, 49, 46, 49, 1753 | 54, 51, 56, 52, 0, 171, 1754 | 81, 0, 0, 5, 0, 0, 1755 | 15, 160, 0, 0, 0, 63, 1756 | 0, 0, 0, 191, 0, 0, 1757 | 0, 0, 0, 0, 0, 0, 1758 | 31, 0, 0, 2, 0, 0, 1759 | 0, 128, 0, 0, 3, 176, 1760 | 31, 0, 0, 2, 0, 0, 1761 | 0, 144, 0, 8, 15, 160, 1762 | 19, 0, 0, 2, 0, 0, 1763 | 8, 128, 0, 0, 85, 176, 1764 | 2, 0, 0, 3, 0, 0, 1765 | 1, 128, 0, 0, 255, 129, 1766 | 0, 0, 0, 160, 1, 0, 1767 | 0, 2, 1, 0, 8, 128, 1768 | 0, 0, 0, 160, 4, 0, 1769 | 0, 4, 0, 0, 1, 128, 1770 | 2, 0, 170, 160, 0, 0, 1771 | 0, 128, 1, 0, 255, 128, 1772 | 2, 0, 0, 3, 0, 0, 1773 | 1, 128, 0, 0, 0, 128, 1774 | 0, 0, 0, 128, 19, 0, 1775 | 0, 2, 0, 0, 6, 128, 1776 | 0, 0, 210, 176, 2, 0, 1777 | 0, 3, 0, 0, 6, 128, 1778 | 0, 0, 228, 129, 0, 0, 1779 | 210, 176, 2, 0, 0, 3, 1780 | 0, 0, 6, 128, 0, 0, 1781 | 228, 128, 0, 0, 0, 160, 1782 | 18, 0, 0, 4, 1, 0, 1783 | 3, 128, 2, 0, 228, 160, 1784 | 0, 0, 228, 176, 0, 0, 1785 | 201, 128, 5, 0, 0, 3, 1786 | 2, 0, 1, 128, 1, 0, 1787 | 0, 128, 1, 0, 170, 160, 1788 | 5, 0, 0, 3, 2, 0, 1789 | 2, 128, 1, 0, 85, 128, 1790 | 1, 0, 255, 160, 66, 0, 1791 | 0, 3, 2, 0, 15, 128, 1792 | 2, 0, 228, 128, 0, 8, 1793 | 228, 160, 4, 0, 0, 4, 1794 | 0, 0, 7, 128, 2, 0, 1795 | 228, 128, 0, 0, 0, 128, 1796 | 0, 0, 85, 160, 4, 0, 1797 | 0, 4, 2, 0, 7, 128, 1798 | 2, 0, 255, 160, 0, 0, 1799 | 228, 128, 1, 0, 255, 128, 1800 | 1, 0, 0, 2, 0, 8, 1801 | 15, 128, 2, 0, 228, 128, 1802 | 255, 255, 0, 0 1803 | }; 1804 | 1805 | //////// End of inlined file: tigr_upscale_d3d9_ps.h //////// 1806 | 1807 | //////// Start of inlined file: tigr_d3d9.c //////// 1808 | 1809 | //#include "tigr_internal.h" 1810 | 1811 | #ifdef TIGR_GAPI_D3D9 1812 | IDirect3D9 *tigrD3D = NULL; 1813 | extern const unsigned char tigrUpscaleVSCode[], tigrUpscalePSCode[]; 1814 | 1815 | void tigrGAPICreate(Tigr *bmp) 1816 | { 1817 | TigrInternal *win = tigrInternal(bmp); 1818 | D3D9Stuff *d3d = &win->d3d9; 1819 | DWORD flags; 1820 | D3DCAPS9 caps; 1821 | 1822 | if (!tigrD3D) 1823 | { 1824 | tigrD3D = Direct3DCreate9(D3D_SDK_VERSION); 1825 | if (!tigrD3D) 1826 | tigrError(bmp, "Cannot initialize Direct3D 9."); 1827 | } 1828 | 1829 | // Initialize D3D 1830 | ZeroMemory(&d3d->params, sizeof(d3d->params)); 1831 | d3d->params.Windowed = TRUE; 1832 | d3d->params.SwapEffect = D3DSWAPEFFECT_DISCARD; 1833 | d3d->params.BackBufferFormat = D3DFMT_A8R8G8B8; 1834 | d3d->params.EnableAutoDepthStencil = FALSE; 1835 | d3d->params.PresentationInterval = D3DPRESENT_INTERVAL_ONE; // TODO- vsync off if fps suffers? 1836 | d3d->params.Flags = 0; 1837 | d3d->params.BackBufferWidth = bmp->w; 1838 | d3d->params.BackBufferHeight = bmp->h; 1839 | 1840 | // Check device caps. 1841 | if (FAILED(IDirect3D9_GetDeviceCaps(tigrD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps))) 1842 | tigrError(bmp, "Cannot query Direct3D 9 capabilities.\n"); 1843 | 1844 | // Check vertex processing mode. Ideally I'd just always use software, 1845 | // but some hardware only supports hardware mode (!) 1846 | flags = D3DCREATE_PUREDEVICE | D3DCREATE_FPU_PRESERVE; 1847 | if (caps.VertexProcessingCaps != 0) 1848 | flags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; 1849 | else 1850 | flags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; 1851 | 1852 | // Create the device. 1853 | if (IDirect3D9_CreateDevice(tigrD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, (HWND)bmp->handle, 1854 | flags, &d3d->params, &d3d->dev)) 1855 | tigrError(bmp, "Cannot create Direct3D 9 device.\n"); 1856 | 1857 | // Create upscaling shaders. 1858 | if (FAILED(IDirect3DDevice9_CreateVertexShader(d3d->dev, (DWORD *)tigrUpscaleVSCode, &d3d->vs)) 1859 | || FAILED(IDirect3DDevice9_CreatePixelShader(d3d->dev, (DWORD *)tigrUpscalePSCode, &d3d->ps))) 1860 | tigrError(bmp, "Cannot create Direct3D 9 shaders.\n"); 1861 | } 1862 | 1863 | void tigrGAPIBegin(Tigr *bmp) 1864 | { 1865 | HRESULT hr; 1866 | TigrInternal *win = tigrInternal(bmp); 1867 | D3D9Stuff *d3d = &win->d3d9; 1868 | 1869 | if (d3d->lost) 1870 | { 1871 | // Check if it's OK to resume work yet. 1872 | if (IDirect3DDevice9_TestCooperativeLevel(d3d->dev) == D3DERR_DEVICELOST) 1873 | return; 1874 | 1875 | hr = IDirect3DDevice9_Reset(d3d->dev, &d3d->params); 1876 | if (hr == D3DERR_DEVICELOST) 1877 | return; 1878 | 1879 | if (FAILED(hr) 1880 | || FAILED(IDirect3DDevice9_CreateTexture(d3d->dev, bmp->w, bmp->h, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, 1881 | D3DPOOL_SYSTEMMEM, &d3d->sysTex[0], NULL)) 1882 | || FAILED(IDirect3DDevice9_CreateTexture(d3d->dev, bmp->w, bmp->h, 1, 0, D3DFMT_A8R8G8B8, 1883 | D3DPOOL_DEFAULT, &d3d->vidTex[0], NULL)) 1884 | || FAILED(IDirect3DDevice9_CreateTexture(d3d->dev, win->widgets->w, win->widgets->h, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, 1885 | D3DPOOL_SYSTEMMEM, &d3d->sysTex[1], NULL)) 1886 | || FAILED(IDirect3DDevice9_CreateTexture(d3d->dev, win->widgets->w, win->widgets->h, 1, 0, D3DFMT_A8R8G8B8, 1887 | D3DPOOL_DEFAULT, &d3d->vidTex[1], NULL))) 1888 | { 1889 | tigrError(bmp, "Error creating Direct3D 9 textures.\n"); 1890 | } 1891 | 1892 | d3d->lost = 0; 1893 | } 1894 | } 1895 | 1896 | void tigrGAPIEnd(Tigr *bmp) 1897 | { 1898 | TigrInternal *win = tigrInternal(bmp); 1899 | D3D9Stuff *d3d = &win->d3d9; 1900 | if (!d3d->lost) 1901 | { 1902 | IDirect3DTexture9_Release(d3d->sysTex[0]); 1903 | IDirect3DTexture9_Release(d3d->vidTex[0]); 1904 | IDirect3DTexture9_Release(d3d->sysTex[1]); 1905 | IDirect3DTexture9_Release(d3d->vidTex[1]); 1906 | IDirect3DDevice9_Reset(d3d->dev, &d3d->params); 1907 | d3d->lost = 1; 1908 | } 1909 | } 1910 | 1911 | void tigrGAPIDestroy(Tigr *bmp) 1912 | { 1913 | TigrInternal *win = tigrInternal(bmp); 1914 | D3D9Stuff *d3d = &win->d3d9; 1915 | 1916 | IDirect3DVertexShader9_Release(d3d->vs); 1917 | IDirect3DPixelShader9_Release(d3d->ps); 1918 | IDirect3DDevice9_Release(d3d->dev); 1919 | } 1920 | 1921 | void tigrGAPIResize(Tigr *bmp, int width, int height) 1922 | { 1923 | TigrInternal *win = tigrInternal(bmp); 1924 | D3D9Stuff *d3d = &win->d3d9; 1925 | 1926 | tigrGAPIEnd(bmp); 1927 | d3d->params.BackBufferWidth = width; 1928 | d3d->params.BackBufferHeight = height; 1929 | tigrGAPIBegin(bmp); 1930 | } 1931 | 1932 | void tigrDxUpdate(IDirect3DDevice9 *dev, IDirect3DTexture9 *sysTex, IDirect3DTexture9 *vidTex, Tigr *bmp) 1933 | { 1934 | D3DLOCKED_RECT rect; 1935 | TPixel *src, *dest; 1936 | int y; 1937 | 1938 | // Lock the system memory texture. 1939 | IDirect3DTexture9_LockRect(sysTex, 0, &rect, NULL, D3DLOCK_DISCARD); 1940 | 1941 | // Copy our bitmap into it. 1942 | src = bmp->pix; 1943 | for (y=0;yh;y++) 1944 | { 1945 | dest = (TPixel *)( (char *)rect.pBits + rect.Pitch*y ); 1946 | memcpy(dest, src, bmp->w*sizeof(TPixel)); 1947 | src += bmp->w; 1948 | } 1949 | 1950 | IDirect3DTexture9_UnlockRect(sysTex, 0); 1951 | 1952 | // Update that into somewhere useful. 1953 | IDirect3DDevice9_UpdateTexture(dev, (IDirect3DBaseTexture9 *)sysTex, (IDirect3DBaseTexture9 *)vidTex); 1954 | } 1955 | 1956 | void tigrDxQuad(IDirect3DDevice9 *dev, IDirect3DTexture9 *tex, int ix0, int iy0, int ix1, int iy1) 1957 | { 1958 | float tri[6][5]; 1959 | float x0 = (float)ix0; 1960 | float y0 = (float)iy0; 1961 | float x1 = (float)ix1; 1962 | float y1 = (float)iy1; 1963 | 1964 | // x y z u v 1965 | tri[0][0] = x0; tri[0][1] = y0; tri[0][2] = 0; tri[0][3] = 0; tri[0][4] = 0; 1966 | tri[1][0] = x1; tri[1][1] = y0; tri[1][2] = 0; tri[1][3] = 1; tri[1][4] = 0; 1967 | tri[2][0] = x0; tri[2][1] = y1; tri[2][2] = 0; tri[2][3] = 0; tri[2][4] = 1; 1968 | 1969 | tri[3][0] = x1; tri[3][1] = y0; tri[3][2] = 0; tri[3][3] = 1; tri[3][4] = 0; 1970 | tri[4][0] = x1; tri[4][1] = y1; tri[4][2] = 0; tri[4][3] = 1; tri[4][4] = 1; 1971 | tri[5][0] = x0; tri[5][1] = y1; tri[5][2] = 0; tri[5][3] = 0; tri[5][4] = 1; 1972 | 1973 | IDirect3DDevice9_SetTexture(dev, 0, (IDirect3DBaseTexture9 *)tex); 1974 | IDirect3DDevice9_DrawPrimitiveUP(dev, D3DPT_TRIANGLELIST, 2, &tri, 5*4); 1975 | } 1976 | 1977 | void tigrGAPIPresent(Tigr *bmp, int dw, int dh) 1978 | { 1979 | TigrInternal *win; 1980 | D3D9Stuff *d3d; 1981 | HWND hWnd; 1982 | HRESULT hr; 1983 | 1984 | win = tigrInternal(bmp); 1985 | d3d = &win->d3d9; 1986 | hWnd = (HWND)bmp->handle; 1987 | 1988 | // Make sure we have a device. 1989 | // If we don't, then sleep to simulate the vsync until it comes back. 1990 | if (d3d->lost) 1991 | { 1992 | tigrGAPIBegin(bmp); 1993 | if (d3d->lost) { 1994 | Sleep(15); 1995 | return; 1996 | } 1997 | } 1998 | 1999 | IDirect3DDevice9_BeginScene(d3d->dev); 2000 | 2001 | // Upload the pixel data. 2002 | tigrDxUpdate(d3d->dev, d3d->sysTex[0], d3d->vidTex[0], bmp); 2003 | tigrDxUpdate(d3d->dev, d3d->sysTex[1], d3d->vidTex[1], win->widgets); 2004 | 2005 | // Set up our upscale shader. 2006 | IDirect3DDevice9_SetRenderState(d3d->dev, D3DRS_ALPHABLENDENABLE, FALSE); 2007 | IDirect3DDevice9_SetVertexShader(d3d->dev, d3d->vs); 2008 | IDirect3DDevice9_SetPixelShader(d3d->dev, d3d->ps); 2009 | IDirect3DDevice9_SetFVF(d3d->dev, D3DFVF_XYZ|D3DFVF_TEX1); 2010 | IDirect3DDevice9_SetSamplerState(d3d->dev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER); 2011 | IDirect3DDevice9_SetSamplerState(d3d->dev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER); 2012 | IDirect3DDevice9_SetSamplerState(d3d->dev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); 2013 | IDirect3DDevice9_SetSamplerState(d3d->dev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); 2014 | 2015 | // Let the shader know about the window size. 2016 | { 2017 | float consts[12]; 2018 | consts[0] = (float)dw; 2019 | consts[1] = (float)dh; 2020 | consts[2] = 1.0f / dw; 2021 | consts[3] = 1.0f / dh; 2022 | consts[4] = (float)bmp->w; 2023 | consts[5] = (float)bmp->h; 2024 | consts[6] = 1.0f / bmp->w; 2025 | consts[7] = 1.0f / bmp->h; 2026 | consts[8] = win->hblur ? 1.0f : 0.0f; 2027 | consts[9] = win->vblur ? 1.0f : 0.0f; 2028 | consts[10] = win->scanlines; 2029 | consts[11] = win->contrast; 2030 | IDirect3DDevice9_SetVertexShaderConstantF(d3d->dev, 0, consts, 3); 2031 | IDirect3DDevice9_SetPixelShaderConstantF(d3d->dev, 0, consts, 3); 2032 | } 2033 | 2034 | // We clear so that a) we fill the border, and b) to let the driver 2035 | // know it can discard the contents. 2036 | IDirect3DDevice9_Clear(d3d->dev, 0, NULL, D3DCLEAR_TARGET, 0, 0, 0); 2037 | 2038 | // Blit the final image to the screen. 2039 | tigrDxQuad(d3d->dev, d3d->vidTex[0], win->pos[0], win->pos[1], win->pos[2], win->pos[3]); 2040 | 2041 | // Draw the widget overlay. 2042 | IDirect3DDevice9_SetRenderState(d3d->dev, D3DRS_ALPHABLENDENABLE, TRUE); 2043 | IDirect3DDevice9_SetRenderState(d3d->dev, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); 2044 | IDirect3DDevice9_SetRenderState(d3d->dev, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); 2045 | tigrDxQuad(d3d->dev, d3d->vidTex[1], 2046 | (int)(dw - win->widgets->w * win->widgetsScale), 0, 2047 | dw, (int)(win->widgets->h * win->widgetsScale)); 2048 | 2049 | // Et fini. 2050 | IDirect3DDevice9_EndScene(d3d->dev); 2051 | hr = IDirect3DDevice9_Present(d3d->dev, NULL, NULL, hWnd, NULL ); 2052 | 2053 | // See if we lost our device. 2054 | if (hr == D3DERR_DEVICELOST) 2055 | tigrGAPIEnd(bmp); 2056 | } 2057 | 2058 | #endif 2059 | 2060 | //////// End of inlined file: tigr_d3d9.c //////// 2061 | 2062 | #endif // _WIN32 2063 | 2064 | //////// Start of inlined file: tigr_upscale_gl_vs.h //////// 2065 | 2066 | // Auto-generated by incbin.pl from tigr_upscale_gl_vs.glsl 2067 | 2068 | const unsigned char tigr_upscale_gl_vs[] = { 2069 | 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x20,0x63,0x6f,0x72, 2070 | 0x65,0x0d,0x0a,0x0d,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x20,0x28,0x6c,0x6f,0x63, 2071 | 0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65, 2072 | 0x63,0x32,0x20,0x70,0x6f,0x73,0x5f,0x69,0x6e,0x3b,0x0d,0x0a,0x6c,0x61,0x79,0x6f, 2073 | 0x75,0x74,0x20,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31, 2074 | 0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x5f,0x69,0x6e,0x3b, 2075 | 0x0d,0x0a,0x0d,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x3b, 2076 | 0x0d,0x0a,0x0d,0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x6d,0x61,0x74,0x34, 2077 | 0x20,0x6d,0x6f,0x64,0x65,0x6c,0x3b,0x0d,0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d, 2078 | 0x20,0x6d,0x61,0x74,0x34,0x20,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x69,0x6f,0x6e, 2079 | 0x3b,0x0d,0x0a,0x0d,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29, 2080 | 0x0d,0x0a,0x7b,0x0d,0x0a,0x09,0x75,0x76,0x20,0x3d,0x20,0x75,0x76,0x5f,0x69,0x6e, 2081 | 0x3b,0x0d,0x0a,0x09,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20, 2082 | 0x3d,0x20,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x69,0x6f,0x6e,0x20,0x2a,0x20,0x6d, 2083 | 0x6f,0x64,0x65,0x6c,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28,0x70,0x6f,0x73,0x5f, 2084 | 0x69,0x6e,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0d,0x0a, 2085 | 0x7d,0x0d,0x0a }; 2086 | 2087 | int tigr_upscale_gl_vs_size = (int)sizeof(tigr_upscale_gl_vs); 2088 | 2089 | 2090 | //////// End of inlined file: tigr_upscale_gl_vs.h //////// 2091 | 2092 | //////// Start of inlined file: tigr_upscale_gl_fs.h //////// 2093 | 2094 | // Auto-generated by incbin.pl from tigr_upscale_gl_fs.glsl 2095 | 2096 | const unsigned char tigr_upscale_gl_fs[] = { 2097 | 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x20,0x63,0x6f,0x72, 2098 | 0x65,0x0d,0x0a,0x0d,0x0a,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x3b, 2099 | 0x0d,0x0a,0x0d,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c, 2100 | 0x6f,0x72,0x3b,0x0d,0x0a,0x0d,0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x73, 2101 | 0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x69,0x6d,0x61,0x67,0x65,0x3b,0x0d, 2102 | 0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x61, 2103 | 0x72,0x61,0x6d,0x65,0x74,0x65,0x72,0x73,0x3b,0x0d,0x0a,0x0d,0x0a,0x76,0x6f,0x69, 2104 | 0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0d,0x0a,0x7b,0x0d,0x0a,0x09,0x76,0x65, 2105 | 0x63,0x32,0x20,0x74,0x65,0x78,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x74,0x65, 2106 | 0x78,0x74,0x75,0x72,0x65,0x53,0x69,0x7a,0x65,0x28,0x69,0x6d,0x61,0x67,0x65,0x2c, 2107 | 0x20,0x30,0x29,0x3b,0x0d,0x0a,0x09,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x5f,0x62, 2108 | 0x6c,0x75,0x72,0x20,0x3d,0x20,0x6d,0x69,0x78,0x28,0x66,0x6c,0x6f,0x6f,0x72,0x28, 2109 | 0x75,0x76,0x20,0x2a,0x20,0x74,0x65,0x78,0x5f,0x73,0x69,0x7a,0x65,0x29,0x20,0x2b, 2110 | 0x20,0x30,0x2e,0x35,0x2c,0x20,0x75,0x76,0x20,0x2a,0x20,0x74,0x65,0x78,0x5f,0x73, 2111 | 0x69,0x7a,0x65,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x65,0x74,0x65,0x72,0x73,0x2e, 2112 | 0x78,0x79,0x29,0x20,0x2f,0x20,0x74,0x65,0x78,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x0d, 2113 | 0x0a,0x09,0x76,0x65,0x63,0x34,0x20,0x63,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75, 2114 | 0x72,0x65,0x28,0x69,0x6d,0x61,0x67,0x65,0x2c,0x20,0x75,0x76,0x5f,0x62,0x6c,0x75, 2115 | 0x72,0x29,0x3b,0x0d,0x0a,0x09,0x63,0x2e,0x72,0x67,0x62,0x20,0x2a,0x3d,0x20,0x6d, 2116 | 0x69,0x78,0x28,0x30,0x2e,0x35,0x2c,0x20,0x31,0x2e,0x30,0x20,0x2d,0x20,0x66,0x72, 2117 | 0x61,0x63,0x74,0x28,0x75,0x76,0x2e,0x79,0x20,0x2a,0x20,0x74,0x65,0x78,0x5f,0x73, 2118 | 0x69,0x7a,0x65,0x2e,0x79,0x29,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x65,0x74,0x65, 2119 | 0x72,0x73,0x2e,0x7a,0x29,0x20,0x2a,0x20,0x32,0x2e,0x30,0x3b,0x20,0x2f,0x2f,0x73, 2120 | 0x63,0x61,0x6e,0x6c,0x69,0x6e,0x65,0x0d,0x0a,0x09,0x63,0x20,0x3d,0x20,0x6d,0x69, 2121 | 0x78,0x28,0x76,0x65,0x63,0x34,0x28,0x30,0x2e,0x35,0x29,0x2c,0x20,0x63,0x2c,0x20, 2122 | 0x70,0x61,0x72,0x61,0x6d,0x65,0x74,0x65,0x72,0x73,0x2e,0x77,0x29,0x3b,0x20,0x2f, 2123 | 0x2f,0x63,0x6f,0x6e,0x74,0x72,0x61,0x73,0x74,0x20,0x0d,0x0a,0x09,0x63,0x6f,0x6c, 2124 | 0x6f,0x72,0x20,0x3d,0x20,0x63,0x3b,0x0d,0x0a,0x7d,0x0d,0x0a }; 2125 | 2126 | int tigr_upscale_gl_fs_size = (int)sizeof(tigr_upscale_gl_fs); 2127 | 2128 | 2129 | //////// End of inlined file: tigr_upscale_gl_fs.h //////// 2130 | 2131 | //////// Start of inlined file: tigr_win.c //////// 2132 | 2133 | //#include "tigr_internal.h" 2134 | #include 2135 | 2136 | // not really windows stuff 2137 | TigrInternal *tigrInternal(Tigr *bmp) 2138 | { 2139 | assert(bmp->handle); 2140 | return (TigrInternal *)(bmp + 1); 2141 | } 2142 | 2143 | #ifdef _WIN32 2144 | #define WIN32_LEAN_AND_MEAN 2145 | #include 2146 | #include 2147 | #include 2148 | #include 2149 | 2150 | #define WIDGET_SCALE 3 2151 | #define WIDGET_FADE 16 2152 | 2153 | int main(int argc, char *argv[]); 2154 | 2155 | #ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION 2156 | HKEY tigrRegKey; 2157 | #endif 2158 | 2159 | static wchar_t *unicode(const char *str) 2160 | { 2161 | int len = MultiByteToWideChar(CP_UTF8, 0, str, -1, 0, 0); 2162 | wchar_t *dest = (wchar_t *)malloc(sizeof(wchar_t) * len); 2163 | MultiByteToWideChar(CP_UTF8, 0, str, -1, dest, len); 2164 | return dest; 2165 | } 2166 | 2167 | void tigrError(Tigr *bmp, const char *message, ...) 2168 | { 2169 | char tmp[1024]; 2170 | 2171 | va_list args; 2172 | va_start(args, message); 2173 | _vsnprintf(tmp, sizeof(tmp), message, args); 2174 | tmp[sizeof(tmp)-1] = 0; 2175 | va_end(args); 2176 | 2177 | MessageBoxW(bmp ? (HWND)bmp->handle : NULL, unicode(tmp), bmp ? tigrInternal(bmp)->wtitle : L"Error", MB_OK|MB_ICONERROR); 2178 | exit(1); 2179 | } 2180 | 2181 | void tigrEnterBorderlessWindowed(Tigr *bmp) 2182 | { 2183 | // Enter borderless windowed mode. 2184 | MONITORINFO mi = { sizeof(mi) }; 2185 | TigrInternal *win = tigrInternal(bmp); 2186 | 2187 | GetWindowRect((HWND)bmp->handle, &win->oldPos); 2188 | 2189 | GetMonitorInfo(MonitorFromWindow((HWND)bmp->handle, MONITOR_DEFAULTTONEAREST), &mi); 2190 | win->dwStyle = WS_VISIBLE | WS_POPUP; 2191 | SetWindowLong((HWND)bmp->handle, GWL_STYLE, win->dwStyle); 2192 | SetWindowPos((HWND)bmp->handle, HWND_TOP, 2193 | mi.rcMonitor.left, 2194 | mi.rcMonitor.top, 2195 | mi.rcMonitor.right - mi.rcMonitor.left, 2196 | mi.rcMonitor.bottom - mi.rcMonitor.top, 2197 | 0); 2198 | } 2199 | 2200 | void tigrLeaveBorderlessWindowed(Tigr *bmp) 2201 | { 2202 | TigrInternal *win = tigrInternal(bmp); 2203 | 2204 | win->dwStyle = WS_VISIBLE | WS_OVERLAPPEDWINDOW; 2205 | SetWindowLong((HWND)bmp->handle, GWL_STYLE, win->dwStyle); 2206 | 2207 | SetWindowPos((HWND)bmp->handle, NULL, 2208 | win->oldPos.left, 2209 | win->oldPos.top, 2210 | win->oldPos.right - win->oldPos.left, 2211 | win->oldPos.bottom - win->oldPos.top, 2212 | 0); 2213 | } 2214 | 2215 | void tigrDxUpdateWidgets(Tigr *bmp, int dw, int dh) 2216 | { 2217 | POINT pt; 2218 | int i, x, clicked=0; 2219 | char str[8]; 2220 | TPixel col; 2221 | TPixel off = tigrRGB(255,255,255); 2222 | TPixel on = tigrRGB(0,200,255); 2223 | TigrInternal *win = tigrInternal(bmp); 2224 | (void)dh; 2225 | 2226 | tigrClear(win->widgets, tigrRGBA(0,0,0,0)); 2227 | 2228 | if (!(win->dwStyle & WS_POPUP)) 2229 | { 2230 | win->widgetsWanted = 0; 2231 | win->widgetAlpha = 0; 2232 | return; 2233 | } 2234 | 2235 | // See if we want to be showing widgets or not. 2236 | GetCursorPos(&pt); 2237 | ScreenToClient((HWND)bmp->handle, &pt); 2238 | if (pt.y == 0) 2239 | win->widgetsWanted = 1; 2240 | if (pt.y > win->widgets->h*WIDGET_SCALE) 2241 | win->widgetsWanted = 0; 2242 | 2243 | // Track the alpha. 2244 | if (win->widgetsWanted) 2245 | win->widgetAlpha = (win->widgetAlpha <= 255-WIDGET_FADE) ? win->widgetAlpha+WIDGET_FADE : 255; 2246 | else 2247 | win->widgetAlpha = (win->widgetAlpha >= WIDGET_FADE) ? win->widgetAlpha-WIDGET_FADE : 0; 2248 | 2249 | // Get relative coords. 2250 | pt.x -= (dw - win->widgets->w*WIDGET_SCALE); 2251 | pt.x /= WIDGET_SCALE; 2252 | pt.y /= WIDGET_SCALE; 2253 | 2254 | tigrClear(win->widgets, tigrRGBA(0,0,0,win->widgetAlpha)); 2255 | 2256 | // Render it. 2257 | for (i=0;i<3;i++) 2258 | { 2259 | switch(i) { 2260 | case 0: str[0] = '_'; str[1] = 0; break; // "_" (minimize) 2261 | case 1: str[0] = 0xEF; str[1] = 0xBF; str[2] = 0xBD; str[3] = 0; break; // "[]" (maximize) 2262 | case 2: str[0] = 0xC3; str[1] = 0x97; str[2] = 0; break; // "x" (close) 2263 | } 2264 | x = win->widgets->w + (i-3)*12; 2265 | if (i == 2) 2266 | off = tigrRGB(255,0,0); 2267 | if (pt.x >= x && pt.x < x+10 && pt.y < win->widgets->h) 2268 | { 2269 | col = on; 2270 | if (GetAsyncKeyState(VK_LBUTTON) & 0x8000) 2271 | clicked |= 1<widgetAlpha; 2276 | tigrPrint(win->widgets, tfont, x, 2, col, str); 2277 | } 2278 | 2279 | if (clicked & 1) 2280 | ShowWindow((HWND)bmp->handle, SW_MINIMIZE); 2281 | if (clicked & 2) 2282 | tigrLeaveBorderlessWindowed(bmp); 2283 | if (clicked & 4) 2284 | SendMessage((HWND)bmp->handle, WM_CLOSE, 0, 0); 2285 | } 2286 | 2287 | void tigrUpdate(Tigr *bmp) 2288 | { 2289 | MSG msg; 2290 | RECT rc; 2291 | int dw, dh; 2292 | TigrInternal *win = tigrInternal(bmp); 2293 | 2294 | if (!win->shown) 2295 | { 2296 | win->shown = 1; 2297 | UpdateWindow((HWND)bmp->handle); 2298 | ShowWindow((HWND)bmp->handle, SW_SHOW); 2299 | } 2300 | 2301 | // Get the window size. 2302 | GetClientRect((HWND)bmp->handle, &rc); 2303 | dw = rc.right - rc.left; 2304 | dh = rc.bottom - rc.top; 2305 | 2306 | // Update the widget overlay. 2307 | tigrDxUpdateWidgets(bmp, dw, dh); 2308 | 2309 | tigrGAPIPresent(bmp, dw, dh); 2310 | 2311 | memcpy(win->prev, win->keys, 256); 2312 | 2313 | // Run the message pump. 2314 | while (PeekMessage(&msg, (HWND)bmp->handle, 0, 0, PM_REMOVE)) 2315 | { 2316 | if (msg.message == WM_QUIT) 2317 | break; 2318 | 2319 | TranslateMessage(&msg); 2320 | DispatchMessage(&msg); 2321 | } 2322 | } 2323 | 2324 | static BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle) 2325 | { 2326 | BOOL fRc; 2327 | RECT rc; 2328 | SetRectEmpty(&rc); 2329 | fRc = AdjustWindowRectEx(&rc, dwStyle, fMenu, dwExStyle); 2330 | if (fRc) { 2331 | prc->left -= rc.left; 2332 | prc->top -= rc.top; 2333 | prc->right -= rc.right; 2334 | prc->bottom -= rc.bottom; 2335 | } 2336 | return fRc; 2337 | } 2338 | 2339 | LRESULT CALLBACK tigrWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 2340 | { 2341 | Tigr *bmp; 2342 | TigrInternal *win = NULL; 2343 | RECT rc; 2344 | int dw, dh; 2345 | 2346 | GetClientRect(hWnd, &rc); 2347 | dw = rc.right - rc.left; 2348 | dh = rc.bottom - rc.top; 2349 | 2350 | bmp = (Tigr *)GetPropW(hWnd, L"Tigr"); 2351 | if (bmp) 2352 | win = tigrInternal(bmp); 2353 | 2354 | switch (message) 2355 | { 2356 | case WM_PAINT: 2357 | tigrGAPIPresent(bmp, dw, dh); 2358 | ValidateRect(hWnd, NULL); 2359 | break; 2360 | case WM_CLOSE: 2361 | if (win) 2362 | win->closed = 1; 2363 | break; 2364 | case WM_GETMINMAXINFO: 2365 | if (bmp) 2366 | { 2367 | MINMAXINFO *info = (MINMAXINFO *)lParam; 2368 | RECT rc; 2369 | rc.left = 0; 2370 | rc.top = 0; 2371 | if (win->flags & TIGR_AUTO) 2372 | { 2373 | rc.right = 32; 2374 | rc.bottom = 32; 2375 | } else { 2376 | int minscale = tigrEnforceScale(1, win->flags); 2377 | rc.right = bmp->w * minscale; 2378 | rc.bottom = bmp->h * minscale; 2379 | } 2380 | AdjustWindowRectEx(&rc, win->dwStyle, FALSE, 0); 2381 | info->ptMinTrackSize.x = rc.right - rc.left; 2382 | info->ptMinTrackSize.y = rc.bottom - rc.top; 2383 | } 2384 | return 0; 2385 | case WM_SIZING: 2386 | if (win) 2387 | { 2388 | // Calculate scale-constrained sizes. 2389 | RECT *rc = (RECT *)lParam; 2390 | int dx, dy; 2391 | UnadjustWindowRectEx(rc, win->dwStyle, FALSE, 0); 2392 | dx = (rc->right - rc->left) % win->scale; 2393 | dy = (rc->bottom - rc->top) % win->scale; 2394 | switch (wParam) { 2395 | case WMSZ_LEFT: rc->left += dx; break; 2396 | case WMSZ_RIGHT: rc->right -= dx; break; 2397 | case WMSZ_TOP: rc->top += dy; break; 2398 | case WMSZ_TOPLEFT: rc->left += dx; rc->top += dy; break; 2399 | case WMSZ_TOPRIGHT: rc->right -= dx; rc->top += dy; break; 2400 | case WMSZ_BOTTOM: rc->bottom -= dy; break; 2401 | case WMSZ_BOTTOMLEFT: rc->left += dx; rc->bottom -= dy; break; 2402 | case WMSZ_BOTTOMRIGHT: rc->right -= dx; rc->bottom -= dy; break; 2403 | } 2404 | AdjustWindowRectEx(rc, win->dwStyle, FALSE, 0); 2405 | } 2406 | return TRUE; 2407 | case WM_SIZE: 2408 | if (win) 2409 | { 2410 | if (wParam != SIZE_MINIMIZED) 2411 | { 2412 | // Detect window size changes and update our bitmap accordingly. 2413 | dw = LOWORD(lParam); 2414 | dh = HIWORD(lParam); 2415 | if (win->flags & TIGR_AUTO) 2416 | { 2417 | tigrResize(bmp, dw/win->scale, dh/win->scale); 2418 | } else { 2419 | win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, dw, dh), win->flags); 2420 | } 2421 | tigrPosition(bmp, win->scale, dw, dh, win->pos); 2422 | tigrGAPIResize(bmp, dw, dh); 2423 | } 2424 | 2425 | // If someone tried to maximize us (e.g. via shortcut launch options), 2426 | // prefer instead to be borderless. 2427 | if (wParam == SIZE_MAXIMIZED) 2428 | { 2429 | ShowWindow((HWND)bmp->handle, SW_NORMAL); 2430 | tigrEnterBorderlessWindowed(bmp); 2431 | } 2432 | } 2433 | return 0; 2434 | #ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION 2435 | case WM_WINDOWPOSCHANGED: 2436 | { 2437 | // Save our position. 2438 | WINDOWPLACEMENT wp = { sizeof(WINDOWPLACEMENT) }; 2439 | GetWindowPlacement(hWnd, &wp); 2440 | if (win->dwStyle & WS_POPUP) 2441 | wp.showCmd = SW_MAXIMIZE; 2442 | RegSetValueExW(tigrRegKey, win->wtitle, 0, REG_BINARY, (BYTE *)&wp, sizeof(wp)); 2443 | return DefWindowProcW(hWnd, message, wParam, lParam); 2444 | } 2445 | #endif 2446 | case WM_ACTIVATE: 2447 | if (win) { 2448 | memset(win->keys, 0, 256); 2449 | memset(win->prev, 0, 256); 2450 | win->lastChar = 0; 2451 | } 2452 | return 0; 2453 | case WM_CHAR: 2454 | if (win) { 2455 | if (wParam == '\r') wParam = '\n'; 2456 | win->lastChar = wParam; 2457 | } 2458 | return DefWindowProcW(hWnd, message, wParam, lParam); 2459 | case WM_MENUCHAR: 2460 | // Disable beep on Alt+Enter 2461 | if (LOWORD(wParam) == VK_RETURN) 2462 | return MNC_CLOSE<<16; 2463 | return DefWindowProcW(hWnd, message, wParam, lParam); 2464 | case WM_SYSKEYDOWN: 2465 | if (win) 2466 | { 2467 | if (wParam == VK_RETURN) 2468 | { 2469 | // Alt+Enter 2470 | if (win->dwStyle & WS_POPUP) 2471 | tigrLeaveBorderlessWindowed(bmp); 2472 | else 2473 | tigrEnterBorderlessWindowed(bmp); 2474 | return 0; 2475 | } 2476 | } 2477 | // fall-thru 2478 | case WM_KEYDOWN: 2479 | if (win) 2480 | win->keys[wParam] = 1; 2481 | return DefWindowProcW(hWnd, message, wParam, lParam); 2482 | case WM_SYSKEYUP: 2483 | // fall-thru 2484 | case WM_KEYUP: 2485 | if (win) 2486 | win->keys[wParam] = 0; 2487 | return DefWindowProcW(hWnd, message, wParam, lParam); 2488 | default: 2489 | return DefWindowProcW(hWnd, message, wParam, lParam); 2490 | } 2491 | return 0; 2492 | } 2493 | 2494 | Tigr *tigrWindow(int w, int h, const char *title, int flags) 2495 | { 2496 | WNDCLASSEXW wcex = {0}; 2497 | int maxW, maxH, scale; 2498 | HWND hWnd; 2499 | DWORD dwStyle; 2500 | RECT rc; 2501 | DWORD err; 2502 | Tigr *bmp; 2503 | TigrInternal *win; 2504 | #ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION 2505 | WINDOWPLACEMENT wp; 2506 | DWORD wpsize = sizeof(wp); 2507 | #endif 2508 | 2509 | wchar_t *wtitle = unicode(title); 2510 | 2511 | // Find our registry key. 2512 | #ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION 2513 | RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\TIGR", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &tigrRegKey, NULL); 2514 | #endif 2515 | 2516 | // Register a window class. 2517 | wcex.cbSize = sizeof(WNDCLASSEXW); 2518 | wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 2519 | wcex.lpfnWndProc = tigrWndProc; 2520 | wcex.hInstance = GetModuleHandle(NULL); 2521 | wcex.hIcon = NULL; 2522 | wcex.hCursor = LoadCursor(NULL, IDC_ARROW); 2523 | wcex.lpszClassName = L"TIGR"; 2524 | RegisterClassExW(&wcex); 2525 | 2526 | if (flags & TIGR_AUTO) 2527 | { 2528 | // Always use a 1:1 pixel size. 2529 | scale = 1; 2530 | } else { 2531 | // See how big we can make it and still fit on-screen. 2532 | maxW = GetSystemMetrics(SM_CXSCREEN) * 3/4; 2533 | maxH = GetSystemMetrics(SM_CYSCREEN) * 3/4; 2534 | scale = tigrCalcScale(w, h, maxW, maxH); 2535 | } 2536 | 2537 | scale = tigrEnforceScale(scale, flags); 2538 | 2539 | // Get the final window size. 2540 | dwStyle = WS_OVERLAPPEDWINDOW; 2541 | rc.left = 0; rc.top = 0; rc.right = w*scale; rc.bottom = h*scale; 2542 | AdjustWindowRect(&rc, dwStyle, FALSE); 2543 | 2544 | // Make a window. 2545 | hWnd = CreateWindowW(L"TIGR", wtitle, dwStyle, 2546 | CW_USEDEFAULT, CW_USEDEFAULT, rc.right-rc.left, rc.bottom-rc.top, 2547 | NULL, NULL, wcex.hInstance, NULL); 2548 | err = GetLastError(); 2549 | if (!hWnd) 2550 | ExitProcess(1); 2551 | 2552 | // Wrap a bitmap around it. 2553 | bmp = tigrBitmap2(w, h, sizeof(TigrInternal)); 2554 | bmp->handle = hWnd; 2555 | 2556 | // Set up the Windows parts. 2557 | win = tigrInternal(bmp); 2558 | win->dwStyle = dwStyle; 2559 | win->wtitle = wtitle; 2560 | win->shown = 0; 2561 | win->closed = 0; 2562 | #ifdef TIGR_GAPI_D3D9 2563 | win->d3d9.lost = 1; 2564 | #endif 2565 | win->scale = scale; 2566 | win->lastChar = 0; 2567 | win->flags = flags; 2568 | 2569 | win->hblur = win->vblur = 0; 2570 | win->scanlines = 0.0f; 2571 | win->contrast = 1.0f; 2572 | 2573 | win->widgetsWanted = 0; 2574 | win->widgetAlpha = 0; 2575 | win->widgetsScale = WIDGET_SCALE; 2576 | win->widgets = tigrBitmap(40, 14); 2577 | 2578 | SetPropW(hWnd, L"Tigr", bmp); 2579 | 2580 | tigrGAPICreate(bmp); 2581 | tigrGAPIBegin(bmp); 2582 | 2583 | // Try and restore our window position. 2584 | #ifndef TIGR_DO_NOT_PRESERVE_WINDOW_POSITION 2585 | if (RegQueryValueExW(tigrRegKey, wtitle, NULL, NULL, (BYTE *)&wp, &wpsize) == ERROR_SUCCESS) 2586 | { 2587 | if (wp.showCmd == SW_MAXIMIZE) 2588 | tigrEnterBorderlessWindowed(bmp); 2589 | else 2590 | SetWindowPlacement(hWnd, &wp); 2591 | } 2592 | #endif 2593 | 2594 | return bmp; 2595 | } 2596 | 2597 | void tigrFree(Tigr *bmp) 2598 | { 2599 | if (bmp->handle) 2600 | { 2601 | TigrInternal *win = tigrInternal(bmp); 2602 | DestroyWindow((HWND)bmp->handle); 2603 | tigrGAPIEnd(bmp); 2604 | tigrGAPIDestroy(bmp); 2605 | free(win->wtitle); 2606 | tigrFree(win->widgets); 2607 | } 2608 | free(bmp->pix); 2609 | free(bmp); 2610 | } 2611 | 2612 | int tigrClosed(Tigr *bmp) 2613 | { 2614 | TigrInternal *win = tigrInternal(bmp); 2615 | int val = win->closed; 2616 | win->closed = 0; 2617 | return val; 2618 | } 2619 | 2620 | float tigrTime() 2621 | { 2622 | static int first = 1; 2623 | static LARGE_INTEGER prev; 2624 | 2625 | LARGE_INTEGER cnt, freq; 2626 | ULONGLONG diff; 2627 | QueryPerformanceCounter(&cnt); 2628 | QueryPerformanceFrequency(&freq); 2629 | 2630 | if (first) 2631 | { 2632 | first = 0; 2633 | prev = cnt; 2634 | } 2635 | 2636 | diff = cnt.QuadPart - prev.QuadPart; 2637 | prev = cnt; 2638 | return (float)(diff / (double)freq.QuadPart); 2639 | } 2640 | 2641 | void tigrMouse(Tigr *bmp, int *x, int *y, int *buttons) 2642 | { 2643 | POINT pt; 2644 | TigrInternal *win; 2645 | 2646 | win = tigrInternal(bmp); 2647 | GetCursorPos(&pt); 2648 | ScreenToClient((HWND)bmp->handle, &pt); 2649 | *x = (pt.x - win->pos[0]) / win->scale; 2650 | *y = (pt.y - win->pos[1]) / win->scale; 2651 | *buttons = 0; 2652 | if (GetFocus() != bmp->handle) 2653 | return; 2654 | if (GetAsyncKeyState(VK_LBUTTON) & 0x8000) *buttons |= 1; 2655 | if (GetAsyncKeyState(VK_MBUTTON) & 0x8000) *buttons |= 2; 2656 | if (GetAsyncKeyState(VK_RBUTTON) & 0x8000) *buttons |= 4; 2657 | } 2658 | 2659 | static int tigrWinVK(int key) 2660 | { 2661 | if (key >= 'A' && key <= 'Z') return key; 2662 | if (key >= '0' && key <= '9') return key; 2663 | switch (key) { 2664 | case TK_BACKSPACE: return VK_BACK; 2665 | case TK_TAB: return VK_TAB; 2666 | case TK_RETURN: return VK_RETURN; 2667 | case TK_SHIFT: return VK_SHIFT; 2668 | case TK_CONTROL: return VK_CONTROL; 2669 | case TK_ALT: return VK_MENU; 2670 | case TK_PAUSE: return VK_PAUSE; 2671 | case TK_CAPSLOCK: return VK_CAPITAL; 2672 | case TK_ESCAPE: return VK_ESCAPE; 2673 | case TK_SPACE: return VK_SPACE; 2674 | case TK_PAGEUP: return VK_PRIOR; 2675 | case TK_PAGEDN: return VK_NEXT; 2676 | case TK_END: return VK_END; 2677 | case TK_HOME: return VK_HOME; 2678 | case TK_LEFT: return VK_LEFT; 2679 | case TK_UP: return VK_UP; 2680 | case TK_RIGHT: return VK_RIGHT; 2681 | case TK_DOWN: return VK_DOWN; 2682 | case TK_INSERT: return VK_INSERT; 2683 | case TK_DELETE: return VK_DELETE; 2684 | case TK_LWIN: return VK_LWIN; 2685 | case TK_RWIN: return VK_RWIN; 2686 | //case TK_APPS: return VK_APPS; // this key doesn't exist on OS X 2687 | case TK_PAD0: return VK_NUMPAD0; 2688 | case TK_PAD1: return VK_NUMPAD1; 2689 | case TK_PAD2: return VK_NUMPAD2; 2690 | case TK_PAD3: return VK_NUMPAD3; 2691 | case TK_PAD4: return VK_NUMPAD4; 2692 | case TK_PAD5: return VK_NUMPAD5; 2693 | case TK_PAD6: return VK_NUMPAD6; 2694 | case TK_PAD7: return VK_NUMPAD7; 2695 | case TK_PAD8: return VK_NUMPAD8; 2696 | case TK_PAD9: return VK_NUMPAD9; 2697 | case TK_PADMUL: return VK_MULTIPLY; 2698 | case TK_PADADD: return VK_ADD; 2699 | case TK_PADENTER: return VK_SEPARATOR; 2700 | case TK_PADSUB: return VK_SUBTRACT; 2701 | case TK_PADDOT: return VK_DECIMAL; 2702 | case TK_PADDIV: return VK_DIVIDE; 2703 | case TK_F1: return VK_F1; 2704 | case TK_F2: return VK_F2; 2705 | case TK_F3: return VK_F3; 2706 | case TK_F4: return VK_F4; 2707 | case TK_F5: return VK_F5; 2708 | case TK_F6: return VK_F6; 2709 | case TK_F7: return VK_F7; 2710 | case TK_F8: return VK_F8; 2711 | case TK_F9: return VK_F9; 2712 | case TK_F10: return VK_F10; 2713 | case TK_F11: return VK_F11; 2714 | case TK_F12: return VK_F12; 2715 | case TK_NUMLOCK: return VK_NUMLOCK; 2716 | case TK_SCROLL: return VK_SCROLL; 2717 | case TK_LSHIFT: return VK_LSHIFT; 2718 | case TK_RSHIFT: return VK_RSHIFT; 2719 | case TK_LCONTROL: return VK_LCONTROL; 2720 | case TK_RCONTROL: return VK_RCONTROL; 2721 | case TK_LALT: return VK_LMENU; 2722 | case TK_RALT: return VK_RMENU; 2723 | case TK_SEMICOLON: return VK_OEM_1; 2724 | case TK_EQUALS: return VK_OEM_PLUS; 2725 | case TK_COMMA: return VK_OEM_COMMA; 2726 | case TK_MINUS: return VK_OEM_MINUS; 2727 | case TK_DOT: return VK_OEM_PERIOD; 2728 | case TK_SLASH: return VK_OEM_2; 2729 | case TK_BACKTICK: return VK_OEM_3; 2730 | case TK_LSQUARE: return VK_OEM_4; 2731 | case TK_BACKSLASH: return VK_OEM_5; 2732 | case TK_RSQUARE: return VK_OEM_6; 2733 | case TK_TICK: return VK_OEM_7; 2734 | } 2735 | return 0; 2736 | } 2737 | 2738 | int tigrKeyDown(Tigr *bmp, int key) 2739 | { 2740 | TigrInternal *win; 2741 | int k = tigrWinVK(key); 2742 | if (GetFocus() != bmp->handle) 2743 | return 0; 2744 | win = tigrInternal(bmp); 2745 | return win->keys[k] && !win->prev[k]; 2746 | } 2747 | 2748 | int tigrKeyHeld(Tigr *bmp, int key) 2749 | { 2750 | TigrInternal *win; 2751 | int k = tigrWinVK(key); 2752 | if (GetFocus() != bmp->handle) 2753 | return 0; 2754 | win = tigrInternal(bmp); 2755 | return win->keys[k]; 2756 | } 2757 | 2758 | int tigrReadChar(Tigr *bmp) 2759 | { 2760 | TigrInternal *win = tigrInternal(bmp); 2761 | int c = win->lastChar; 2762 | win->lastChar = 0; 2763 | return c; 2764 | } 2765 | 2766 | // We supply our own WinMain and just chain through to the user's 2767 | // real entry point. 2768 | #ifdef UNICODE 2769 | int CALLBACK wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) 2770 | #else 2771 | int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 2772 | #endif 2773 | { 2774 | int n, argc; 2775 | LPWSTR *wargv = CommandLineToArgvW(GetCommandLineW(), &argc); 2776 | char **argv = (char **)calloc(argc+1, sizeof(int)); 2777 | 2778 | (void)hInstance; (void)hPrevInstance; (void)lpCmdLine; (void)nCmdShow; 2779 | 2780 | for (n=0;n 2800 | #ifdef TARGET_OS_MAC 2801 | 2802 | #include 2803 | #include 2804 | #include 2805 | #include 2806 | #include 2807 | #include 2808 | #include 2809 | #include 2810 | 2811 | #include 2812 | #include 2813 | #include 2814 | #include 2815 | #include 2816 | #include 2817 | #include 2818 | 2819 | // maybe this is available somewhere in objc runtime? 2820 | #if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64 2821 | #define NSIntegerEncoding "q" 2822 | #define NSUIntegerEncoding "L" 2823 | #else 2824 | #define NSIntegerEncoding "i" 2825 | #define NSUIntegerEncoding "I" 2826 | #endif 2827 | 2828 | #ifdef __OBJC__ 2829 | #import 2830 | #else 2831 | // this is how they are defined originally 2832 | #include 2833 | #include 2834 | typedef CGPoint NSPoint; 2835 | typedef CGSize NSSize; 2836 | typedef CGRect NSRect; 2837 | 2838 | extern id NSApp; 2839 | extern id const NSDefaultRunLoopMode; 2840 | #endif 2841 | 2842 | #if defined(__OBJC__) && __has_feature(objc_arc) 2843 | //#define ARC_AVAILABLE 2844 | #error "Can't compile as objective-c code just yet! (see autorelease pool todo below)" 2845 | #endif 2846 | 2847 | // ABI is a bit different between platforms 2848 | #ifdef __arm64__ 2849 | #define abi_objc_msgSend_stret objc_msgSend 2850 | #else 2851 | #define abi_objc_msgSend_stret objc_msgSend_stret 2852 | #endif 2853 | #ifdef __i386__ 2854 | #define abi_objc_msgSend_fpret objc_msgSend_fpret 2855 | #else 2856 | #define abi_objc_msgSend_fpret objc_msgSend 2857 | #endif 2858 | 2859 | #define objc_msgSend_id ((id (*)(id, SEL))objc_msgSend) 2860 | #define objc_msgSend_void ((void (*)(id, SEL))objc_msgSend) 2861 | #define objc_msgSend_void_id ((void (*)(id, SEL, id))objc_msgSend) 2862 | #define objc_msgSend_void_bool ((void (*)(id, SEL, BOOL))objc_msgSend) 2863 | #define objc_msgSend_id_const_char ((id (*)(id, SEL, const char*))objc_msgSend) 2864 | 2865 | bool terminated = false; 2866 | 2867 | // we gonna construct objective-c class by hand in runtime, so wow, so hacker! 2868 | NSUInteger applicationShouldTerminate(id self, SEL _sel, id sender) 2869 | { 2870 | terminated = true; 2871 | return 0; 2872 | } 2873 | 2874 | void windowWillClose(id self, SEL _sel, id notification) 2875 | { 2876 | NSUInteger value = true; 2877 | object_setInstanceVariable(self, "closed", (void*)value); 2878 | } 2879 | 2880 | void windowDidBecomeKey(id self, SEL _sel, id notification) 2881 | { 2882 | TigrInternal * win; 2883 | Tigr * bmp = 0; 2884 | object_getInstanceVariable(self, "tigrHandle", (void**)&bmp); 2885 | win = bmp ? tigrInternal(bmp) : NULL; 2886 | 2887 | if(win) 2888 | { 2889 | memset(win->keys, 0, 256); 2890 | memset(win->prev, 0, 256); 2891 | win->lastChar = 0; 2892 | win->mouseButtons = 0; 2893 | } 2894 | } 2895 | 2896 | bool _tigrCocoaIsWindowClosed(id window) 2897 | { 2898 | id wdg = objc_msgSend_id(window, sel_registerName("delegate")); 2899 | if(!wdg) 2900 | return false; 2901 | NSUInteger value = 0; 2902 | object_getInstanceVariable(wdg, "closed", (void**)&value); 2903 | return value ? true : false; 2904 | } 2905 | 2906 | static bool tigrOSXInited = false; 2907 | static id autoreleasePool = NULL; 2908 | 2909 | void _tigrCleanupOSX() 2910 | { 2911 | #ifdef ARC_AVAILABLE 2912 | // TODO autorelease pool 2913 | #else 2914 | objc_msgSend_void(autoreleasePool, sel_registerName("drain")); 2915 | #endif 2916 | } 2917 | 2918 | void tigrInitOSX() 2919 | { 2920 | if(tigrOSXInited) 2921 | return; 2922 | 2923 | #ifdef ARC_AVAILABLE 2924 | // TODO and what do we do now? it's a bit too tricky to use @autoreleasepool here 2925 | #error TODO this code should be compiled as C for now 2926 | #else 2927 | //would be nice to use objc_autoreleasePoolPush instead, but it's not publically available in the headers 2928 | id poolAlloc = objc_msgSend_id((id)objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); 2929 | autoreleasePool = objc_msgSend_id(poolAlloc, sel_registerName("init")); 2930 | atexit(&_tigrCleanupOSX); 2931 | #endif 2932 | 2933 | objc_msgSend_id((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication")); 2934 | ((void (*)(id, SEL, NSInteger))objc_msgSend)(NSApp, sel_registerName("setActivationPolicy:"), 0); 2935 | 2936 | Class appDelegateClass = objc_allocateClassPair((Class)objc_getClass("NSObject"), "AppDelegate", 0); 2937 | bool resultAddProtoc = class_addProtocol(appDelegateClass, objc_getProtocol("NSApplicationDelegate")); 2938 | assert(resultAddProtoc); 2939 | bool resultAddMethod = class_addMethod(appDelegateClass, sel_registerName("applicationShouldTerminate:"), (IMP)applicationShouldTerminate, NSUIntegerEncoding "@:@"); 2940 | assert(resultAddMethod); 2941 | id dgAlloc = objc_msgSend_id((id)appDelegateClass, sel_registerName("alloc")); 2942 | id dg = objc_msgSend_id(dgAlloc, sel_registerName("init")); 2943 | #ifndef ARC_AVAILABLE 2944 | objc_msgSend_void(dg, sel_registerName("autorelease")); 2945 | #endif 2946 | 2947 | objc_msgSend_void_id(NSApp, sel_registerName("setDelegate:"), dg); 2948 | objc_msgSend_void(NSApp, sel_registerName("finishLaunching")); 2949 | 2950 | id menubarAlloc = objc_msgSend_id((id)objc_getClass("NSMenu"), sel_registerName("alloc")); 2951 | id menubar = objc_msgSend_id(menubarAlloc, sel_registerName("init")); 2952 | #ifndef ARC_AVAILABLE 2953 | objc_msgSend_void(menubar, sel_registerName("autorelease")); 2954 | #endif 2955 | 2956 | id appMenuItemAlloc = objc_msgSend_id((id)objc_getClass("NSMenuItem"), sel_registerName("alloc")); 2957 | id appMenuItem = objc_msgSend_id(appMenuItemAlloc, sel_registerName("init")); 2958 | #ifndef ARC_AVAILABLE 2959 | objc_msgSend_void(appMenuItem, sel_registerName("autorelease")); 2960 | #endif 2961 | 2962 | objc_msgSend_void_id(menubar, sel_registerName("addItem:"), appMenuItem); 2963 | ((id (*)(id, SEL, id))objc_msgSend)(NSApp, sel_registerName("setMainMenu:"), menubar); 2964 | 2965 | id appMenuAlloc = objc_msgSend_id((id)objc_getClass("NSMenu"), sel_registerName("alloc")); 2966 | id appMenu = objc_msgSend_id(appMenuAlloc, sel_registerName("init")); 2967 | #ifndef ARC_AVAILABLE 2968 | objc_msgSend_void(appMenu, sel_registerName("autorelease")); 2969 | #endif 2970 | 2971 | id processInfo = objc_msgSend_id((id)objc_getClass("NSProcessInfo"), sel_registerName("processInfo")); 2972 | id appName = objc_msgSend_id(processInfo, sel_registerName("processName")); 2973 | 2974 | id quitTitlePrefixString = objc_msgSend_id_const_char((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "Quit "); 2975 | id quitTitle = ((id (*)(id, SEL, id))objc_msgSend)(quitTitlePrefixString, sel_registerName("stringByAppendingString:"), appName); 2976 | 2977 | id quitMenuItemKey = objc_msgSend_id_const_char((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "q"); 2978 | id quitMenuItemAlloc = objc_msgSend_id((id)objc_getClass("NSMenuItem"), sel_registerName("alloc")); 2979 | id quitMenuItem = ((id (*)(id, SEL, id, SEL, id))objc_msgSend)(quitMenuItemAlloc, sel_registerName("initWithTitle:action:keyEquivalent:"), quitTitle, sel_registerName("terminate:"), quitMenuItemKey); 2980 | #ifndef ARC_AVAILABLE 2981 | objc_msgSend_void(quitMenuItem, sel_registerName("autorelease")); 2982 | #endif 2983 | 2984 | objc_msgSend_void_id(appMenu, sel_registerName("addItem:"), quitMenuItem); 2985 | objc_msgSend_void_id(appMenuItem, sel_registerName("setSubmenu:"), appMenu); 2986 | 2987 | tigrOSXInited = true; 2988 | } 2989 | 2990 | void tigrError(Tigr *bmp, const char *message, ...) 2991 | { 2992 | char tmp[1024]; 2993 | 2994 | va_list args; 2995 | va_start(args, message); 2996 | vsnprintf(tmp, sizeof(tmp), message, args); 2997 | tmp[sizeof(tmp)-1] = 0; 2998 | va_end(args); 2999 | 3000 | CFStringRef header = CFStringCreateWithCString(NULL, "Error", kCFStringEncodingUTF8); 3001 | CFStringRef msg = CFStringCreateWithCString(NULL, tmp, kCFStringEncodingUTF8); 3002 | CFUserNotificationDisplayNotice(0.0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, header, msg, NULL); 3003 | CFRelease(header); 3004 | CFRelease(msg); 3005 | exit(1); 3006 | } 3007 | 3008 | NSSize _tigrCocoaWindowSize(id window) 3009 | { 3010 | id contentView = objc_msgSend_id(window, sel_registerName("contentView")); 3011 | NSRect rect = ((NSRect (*)(id, SEL))abi_objc_msgSend_stret)(contentView, sel_registerName("frame")); 3012 | rect = ((NSRect (*)(id, SEL, NSRect))abi_objc_msgSend_stret)(contentView, sel_registerName("convertRectToBacking:"), rect); 3013 | 3014 | return rect.size; 3015 | } 3016 | 3017 | TigrInternal * _tigrInternalCocoa(id window) 3018 | { 3019 | if(!window) 3020 | return NULL; 3021 | 3022 | id wdg = objc_msgSend_id(window, sel_registerName("delegate")); 3023 | if(!wdg) 3024 | return NULL; 3025 | 3026 | Tigr * bmp = 0; 3027 | object_getInstanceVariable(wdg, "tigrHandle", (void**)&bmp); 3028 | return bmp ? tigrInternal(bmp) : NULL; 3029 | } 3030 | 3031 | Tigr *tigrWindow(int w, int h, const char *title, int flags) 3032 | { 3033 | int scale; 3034 | Tigr *bmp; 3035 | TigrInternal *win; 3036 | 3037 | tigrInitOSX(); 3038 | 3039 | if (flags & TIGR_AUTO) 3040 | { 3041 | // Always use a 1:1 pixel size. 3042 | scale = 1; 3043 | } else { 3044 | // See how big we can make it and still fit on-screen. 3045 | CGRect mainMonitor = CGDisplayBounds(CGMainDisplayID()); 3046 | int maxW = CGRectGetHeight(mainMonitor) * 3/4; 3047 | int maxH = CGRectGetWidth(mainMonitor) * 3/4; 3048 | scale = tigrCalcScale(w, h, maxW, maxH); 3049 | } 3050 | 3051 | scale = tigrEnforceScale(scale, flags); 3052 | 3053 | NSRect rect = {{0, 0}, {w * scale, h * scale}}; 3054 | id windowAlloc = objc_msgSend_id((id)objc_getClass("NSWindow"), sel_registerName("alloc")); 3055 | id window = ((id (*)(id, SEL, NSRect, NSUInteger, NSUInteger, BOOL))objc_msgSend)(windowAlloc, sel_registerName("initWithContentRect:styleMask:backing:defer:"), rect, 15, 2, NO); 3056 | #ifndef ARC_AVAILABLE 3057 | objc_msgSend_void(window, sel_registerName("autorelease")); 3058 | #endif 3059 | 3060 | // when we are not using ARC, than window will be added to autorelease pool 3061 | // so if we close it by hand (pressing red button), we don't want it to be released for us 3062 | // so it will be released by autorelease pool later 3063 | objc_msgSend_void_bool(window, sel_registerName("setReleasedWhenClosed:"), NO); 3064 | 3065 | Class WindowDelegateClass = objc_allocateClassPair((Class)objc_getClass("NSObject"), "WindowDelegate", 0); 3066 | bool resultAddProtoc = class_addProtocol(WindowDelegateClass, objc_getProtocol("NSWindowDelegate")); 3067 | assert(resultAddProtoc); 3068 | bool resultAddIvar = class_addIvar(WindowDelegateClass, "closed", sizeof(NSUInteger), rint(log2(sizeof(NSUInteger))), NSUIntegerEncoding); 3069 | assert(resultAddIvar); 3070 | resultAddIvar = class_addIvar(WindowDelegateClass, "tigrHandle", sizeof(void*), rint(log2(sizeof(void*))), "ˆv"); 3071 | assert(resultAddIvar); 3072 | bool resultAddMethod = class_addMethod(WindowDelegateClass, sel_registerName("windowWillClose:"), (IMP)windowWillClose, "v@:@"); 3073 | assert(resultAddMethod); 3074 | resultAddMethod = class_addMethod(WindowDelegateClass, sel_registerName("windowDidBecomeKey:"), (IMP)windowDidBecomeKey, "v@:@"); 3075 | assert(resultAddMethod); 3076 | id wdgAlloc = objc_msgSend_id((id)WindowDelegateClass, sel_registerName("alloc")); 3077 | id wdg = objc_msgSend_id(wdgAlloc, sel_registerName("init")); 3078 | #ifndef ARC_AVAILABLE 3079 | objc_msgSend_void(wdg, sel_registerName("autorelease")); 3080 | #endif 3081 | 3082 | objc_msgSend_void_id(window, sel_registerName("setDelegate:"), wdg); 3083 | 3084 | id contentView = objc_msgSend_id(window, sel_registerName("contentView")); 3085 | 3086 | if(flags & TIGR_RETINA) 3087 | objc_msgSend_void_bool(contentView, sel_registerName("setWantsBestResolutionOpenGLSurface:"), YES); 3088 | 3089 | NSPoint point = {20, 20}; 3090 | ((void (*)(id, SEL, NSPoint))objc_msgSend)(window, sel_registerName("cascadeTopLeftFromPoint:"), point); 3091 | 3092 | id titleString = objc_msgSend_id_const_char((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), title); 3093 | objc_msgSend_void_id(window, sel_registerName("setTitle:"), titleString); 3094 | 3095 | uint32_t glAttributes[] = 3096 | { 3097 | 8, 24, // NSOpenGLPFAColorSize, 24, 3098 | 11, 8, // NSOpenGLPFAAlphaSize, 8, 3099 | 5, // NSOpenGLPFADoubleBuffer, 3100 | 73, // NSOpenGLPFAAccelerated, 3101 | //72, // NSOpenGLPFANoRecovery, 3102 | //55, 1, // NSOpenGLPFASampleBuffers, 1, 3103 | //56, 4, // NSOpenGLPFASamples, 4, 3104 | 99, 0x3200, // NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, 3105 | 0 3106 | }; 3107 | 3108 | id pixelFormatAlloc = objc_msgSend_id((id)objc_getClass("NSOpenGLPixelFormat"), sel_registerName("alloc")); 3109 | id pixelFormat = ((id (*)(id, SEL, const uint32_t*))objc_msgSend)(pixelFormatAlloc, sel_registerName("initWithAttributes:"), glAttributes); 3110 | #ifndef ARC_AVAILABLE 3111 | objc_msgSend_void(pixelFormat, sel_registerName("autorelease")); 3112 | #endif 3113 | 3114 | id openGLContextAlloc = objc_msgSend_id((id)objc_getClass("NSOpenGLContext"), sel_registerName("alloc")); 3115 | id openGLContext = ((id (*)(id, SEL, id, id))objc_msgSend)(openGLContextAlloc, sel_registerName("initWithFormat:shareContext:"), pixelFormat, nil); 3116 | #ifndef ARC_AVAILABLE 3117 | objc_msgSend_void(openGLContext, sel_registerName("autorelease")); 3118 | #endif 3119 | 3120 | objc_msgSend_void_id(openGLContext, sel_registerName("setView:"), contentView); 3121 | objc_msgSend_void_id(window, sel_registerName("makeKeyAndOrderFront:"), window); 3122 | objc_msgSend_void_bool(window, sel_registerName("setAcceptsMouseMovedEvents:"), YES); 3123 | 3124 | id blackColor = objc_msgSend_id((id)objc_getClass("NSColor"), sel_registerName("blackColor")); 3125 | objc_msgSend_void_id(window, sel_registerName("setBackgroundColor:"), blackColor); 3126 | 3127 | // TODO do we really need this? 3128 | objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), YES); 3129 | 3130 | // Wrap a bitmap around it. 3131 | NSSize windowSize = _tigrCocoaWindowSize(window); 3132 | bmp = tigrBitmap2(windowSize.width / scale, windowSize.height / scale, sizeof(TigrInternal)); 3133 | bmp->handle = window; 3134 | 3135 | // Set the handle 3136 | object_setInstanceVariable(wdg, "tigrHandle", (void*)bmp); 3137 | 3138 | // Set up the Windows parts. 3139 | win = tigrInternal(bmp); 3140 | win->glContext = openGLContext; 3141 | win->shown = 0; 3142 | win->closed = 0; 3143 | win->scale = scale; 3144 | win->lastChar = 0; 3145 | win->flags = flags; 3146 | win->hblur = win->vblur = 0; 3147 | win->scanlines = 0.0f; 3148 | win->contrast = 1.0f; 3149 | win->widgetsWanted = 0; 3150 | win->widgetAlpha = 0; 3151 | win->widgetsScale = 0; 3152 | win->widgets = tigrBitmap(40, 14); 3153 | win->gl.gl_legacy = 0; 3154 | win->mouseButtons = 0; 3155 | 3156 | tigrPosition(bmp, win->scale, bmp->w, bmp->h, win->pos); 3157 | 3158 | objc_msgSend_void(openGLContext, sel_registerName("makeCurrentContext")); 3159 | tigrGAPICreate(bmp); 3160 | tigrGAPIBegin(bmp); 3161 | tigrGAPIResize(bmp, bmp->w, bmp->h); 3162 | 3163 | return bmp; 3164 | } 3165 | 3166 | void tigrFree(Tigr *bmp) 3167 | { 3168 | if(bmp->handle) 3169 | { 3170 | TigrInternal * win = tigrInternal(bmp); 3171 | objc_msgSend_void((id)win->glContext, sel_registerName("makeCurrentContext")); 3172 | tigrGAPIEnd(bmp); 3173 | tigrGAPIDestroy(bmp); 3174 | tigrFree(win->widgets); 3175 | 3176 | id window = (id)bmp->handle; 3177 | if(!_tigrCocoaIsWindowClosed(window) && !terminated) 3178 | objc_msgSend_void(window, sel_registerName("close")); 3179 | } 3180 | free(bmp->pix); 3181 | free(bmp); 3182 | } 3183 | 3184 | uint8_t _tigrKeyFromOSX(uint16_t key) 3185 | { 3186 | // from Carbon HIToolbox/Events.h 3187 | enum 3188 | { 3189 | kVK_ANSI_A = 0x00, 3190 | kVK_ANSI_S = 0x01, 3191 | kVK_ANSI_D = 0x02, 3192 | kVK_ANSI_F = 0x03, 3193 | kVK_ANSI_H = 0x04, 3194 | kVK_ANSI_G = 0x05, 3195 | kVK_ANSI_Z = 0x06, 3196 | kVK_ANSI_X = 0x07, 3197 | kVK_ANSI_C = 0x08, 3198 | kVK_ANSI_V = 0x09, 3199 | kVK_ANSI_B = 0x0B, 3200 | kVK_ANSI_Q = 0x0C, 3201 | kVK_ANSI_W = 0x0D, 3202 | kVK_ANSI_E = 0x0E, 3203 | kVK_ANSI_R = 0x0F, 3204 | kVK_ANSI_Y = 0x10, 3205 | kVK_ANSI_T = 0x11, 3206 | kVK_ANSI_1 = 0x12, 3207 | kVK_ANSI_2 = 0x13, 3208 | kVK_ANSI_3 = 0x14, 3209 | kVK_ANSI_4 = 0x15, 3210 | kVK_ANSI_6 = 0x16, 3211 | kVK_ANSI_5 = 0x17, 3212 | kVK_ANSI_Equal = 0x18, 3213 | kVK_ANSI_9 = 0x19, 3214 | kVK_ANSI_7 = 0x1A, 3215 | kVK_ANSI_Minus = 0x1B, 3216 | kVK_ANSI_8 = 0x1C, 3217 | kVK_ANSI_0 = 0x1D, 3218 | kVK_ANSI_RightBracket = 0x1E, 3219 | kVK_ANSI_O = 0x1F, 3220 | kVK_ANSI_U = 0x20, 3221 | kVK_ANSI_LeftBracket = 0x21, 3222 | kVK_ANSI_I = 0x22, 3223 | kVK_ANSI_P = 0x23, 3224 | kVK_ANSI_L = 0x25, 3225 | kVK_ANSI_J = 0x26, 3226 | kVK_ANSI_Quote = 0x27, 3227 | kVK_ANSI_K = 0x28, 3228 | kVK_ANSI_Semicolon = 0x29, 3229 | kVK_ANSI_Backslash = 0x2A, 3230 | kVK_ANSI_Comma = 0x2B, 3231 | kVK_ANSI_Slash = 0x2C, 3232 | kVK_ANSI_N = 0x2D, 3233 | kVK_ANSI_M = 0x2E, 3234 | kVK_ANSI_Period = 0x2F, 3235 | kVK_ANSI_Grave = 0x32, 3236 | kVK_ANSI_KeypadDecimal = 0x41, 3237 | kVK_ANSI_KeypadMultiply = 0x43, 3238 | kVK_ANSI_KeypadPlus = 0x45, 3239 | kVK_ANSI_KeypadClear = 0x47, 3240 | kVK_ANSI_KeypadDivide = 0x4B, 3241 | kVK_ANSI_KeypadEnter = 0x4C, 3242 | kVK_ANSI_KeypadMinus = 0x4E, 3243 | kVK_ANSI_KeypadEquals = 0x51, 3244 | kVK_ANSI_Keypad0 = 0x52, 3245 | kVK_ANSI_Keypad1 = 0x53, 3246 | kVK_ANSI_Keypad2 = 0x54, 3247 | kVK_ANSI_Keypad3 = 0x55, 3248 | kVK_ANSI_Keypad4 = 0x56, 3249 | kVK_ANSI_Keypad5 = 0x57, 3250 | kVK_ANSI_Keypad6 = 0x58, 3251 | kVK_ANSI_Keypad7 = 0x59, 3252 | kVK_ANSI_Keypad8 = 0x5B, 3253 | kVK_ANSI_Keypad9 = 0x5C, 3254 | kVK_Return = 0x24, 3255 | kVK_Tab = 0x30, 3256 | kVK_Space = 0x31, 3257 | kVK_Delete = 0x33, 3258 | kVK_Escape = 0x35, 3259 | kVK_Command = 0x37, 3260 | kVK_Shift = 0x38, 3261 | kVK_CapsLock = 0x39, 3262 | kVK_Option = 0x3A, 3263 | kVK_Control = 0x3B, 3264 | kVK_RightShift = 0x3C, 3265 | kVK_RightOption = 0x3D, 3266 | kVK_RightControl = 0x3E, 3267 | kVK_Function = 0x3F, 3268 | kVK_F17 = 0x40, 3269 | kVK_VolumeUp = 0x48, 3270 | kVK_VolumeDown = 0x49, 3271 | kVK_Mute = 0x4A, 3272 | kVK_F18 = 0x4F, 3273 | kVK_F19 = 0x50, 3274 | kVK_F20 = 0x5A, 3275 | kVK_F5 = 0x60, 3276 | kVK_F6 = 0x61, 3277 | kVK_F7 = 0x62, 3278 | kVK_F3 = 0x63, 3279 | kVK_F8 = 0x64, 3280 | kVK_F9 = 0x65, 3281 | kVK_F11 = 0x67, 3282 | kVK_F13 = 0x69, 3283 | kVK_F16 = 0x6A, 3284 | kVK_F14 = 0x6B, 3285 | kVK_F10 = 0x6D, 3286 | kVK_F12 = 0x6F, 3287 | kVK_F15 = 0x71, 3288 | kVK_Help = 0x72, 3289 | kVK_Home = 0x73, 3290 | kVK_PageUp = 0x74, 3291 | kVK_ForwardDelete = 0x75, 3292 | kVK_F4 = 0x76, 3293 | kVK_End = 0x77, 3294 | kVK_F2 = 0x78, 3295 | kVK_PageDown = 0x79, 3296 | kVK_F1 = 0x7A, 3297 | kVK_LeftArrow = 0x7B, 3298 | kVK_RightArrow = 0x7C, 3299 | kVK_DownArrow = 0x7D, 3300 | kVK_UpArrow = 0x7E 3301 | }; 3302 | 3303 | switch(key) 3304 | { 3305 | case kVK_ANSI_Q: return 'Q'; 3306 | case kVK_ANSI_W: return 'W'; 3307 | case kVK_ANSI_E: return 'E'; 3308 | case kVK_ANSI_R: return 'R'; 3309 | case kVK_ANSI_T: return 'T'; 3310 | case kVK_ANSI_Y: return 'Y'; 3311 | case kVK_ANSI_U: return 'U'; 3312 | case kVK_ANSI_I: return 'I'; 3313 | case kVK_ANSI_O: return 'O'; 3314 | case kVK_ANSI_P: return 'P'; 3315 | case kVK_ANSI_A: return 'A'; 3316 | case kVK_ANSI_S: return 'S'; 3317 | case kVK_ANSI_D: return 'D'; 3318 | case kVK_ANSI_F: return 'F'; 3319 | case kVK_ANSI_G: return 'G'; 3320 | case kVK_ANSI_H: return 'H'; 3321 | case kVK_ANSI_J: return 'J'; 3322 | case kVK_ANSI_K: return 'K'; 3323 | case kVK_ANSI_L: return 'L'; 3324 | case kVK_ANSI_Z: return 'Z'; 3325 | case kVK_ANSI_X: return 'X'; 3326 | case kVK_ANSI_C: return 'C'; 3327 | case kVK_ANSI_V: return 'V'; 3328 | case kVK_ANSI_B: return 'B'; 3329 | case kVK_ANSI_N: return 'N'; 3330 | case kVK_ANSI_M: return 'M'; 3331 | case kVK_ANSI_0: return '0'; 3332 | case kVK_ANSI_1: return '1'; 3333 | case kVK_ANSI_2: return '2'; 3334 | case kVK_ANSI_3: return '3'; 3335 | case kVK_ANSI_4: return '4'; 3336 | case kVK_ANSI_5: return '5'; 3337 | case kVK_ANSI_6: return '6'; 3338 | case kVK_ANSI_7: return '7'; 3339 | case kVK_ANSI_8: return '8'; 3340 | case kVK_ANSI_9: return '9'; 3341 | case kVK_ANSI_Keypad0: return TK_PAD0; 3342 | case kVK_ANSI_Keypad1: return TK_PAD1; 3343 | case kVK_ANSI_Keypad2: return TK_PAD2; 3344 | case kVK_ANSI_Keypad3: return TK_PAD3; 3345 | case kVK_ANSI_Keypad4: return TK_PAD4; 3346 | case kVK_ANSI_Keypad5: return TK_PAD5; 3347 | case kVK_ANSI_Keypad6: return TK_PAD6; 3348 | case kVK_ANSI_Keypad7: return TK_PAD7; 3349 | case kVK_ANSI_Keypad8: return TK_PAD8; 3350 | case kVK_ANSI_Keypad9: return TK_PAD9; 3351 | case kVK_ANSI_KeypadMultiply: return TK_PADMUL; 3352 | case kVK_ANSI_KeypadPlus: return TK_PADADD; 3353 | case kVK_ANSI_KeypadEnter: return TK_PADENTER; 3354 | case kVK_ANSI_KeypadMinus: return TK_PADSUB; 3355 | case kVK_ANSI_KeypadDecimal: return TK_PADDOT; 3356 | case kVK_ANSI_KeypadDivide: return TK_PADDIV; 3357 | case kVK_F1: return TK_F1; 3358 | case kVK_F2: return TK_F2; 3359 | case kVK_F3: return TK_F3; 3360 | case kVK_F4: return TK_F4; 3361 | case kVK_F5: return TK_F5; 3362 | case kVK_F6: return TK_F6; 3363 | case kVK_F7: return TK_F7; 3364 | case kVK_F8: return TK_F8; 3365 | case kVK_F9: return TK_F9; 3366 | case kVK_F10: return TK_F10; 3367 | case kVK_F11: return TK_F11; 3368 | case kVK_F12: return TK_F12; 3369 | case kVK_Shift: return TK_LSHIFT; 3370 | case kVK_Control: return TK_LCONTROL; 3371 | case kVK_Option: return TK_LALT; 3372 | case kVK_CapsLock: return TK_CAPSLOCK; 3373 | case kVK_Command: return TK_LWIN; 3374 | case kVK_Command - 1: return TK_RWIN; 3375 | case kVK_RightShift: return TK_RSHIFT; 3376 | case kVK_RightControl: return TK_RCONTROL; 3377 | case kVK_RightOption: return TK_RALT; 3378 | case kVK_Delete: return TK_BACKSPACE; 3379 | case kVK_Tab: return TK_TAB; 3380 | case kVK_Return: return TK_RETURN; 3381 | case kVK_Escape: return TK_ESCAPE; 3382 | case kVK_Space: return TK_SPACE; 3383 | case kVK_PageUp: return TK_PAGEUP; 3384 | case kVK_PageDown: return TK_PAGEDN; 3385 | case kVK_End: return TK_END; 3386 | case kVK_Home: return TK_HOME; 3387 | case kVK_LeftArrow: return TK_LEFT; 3388 | case kVK_UpArrow: return TK_UP; 3389 | case kVK_RightArrow: return TK_RIGHT; 3390 | case kVK_DownArrow: return TK_DOWN; 3391 | case kVK_Help: return TK_INSERT; 3392 | case kVK_ForwardDelete: return TK_DELETE; 3393 | case kVK_F14: return TK_SCROLL; 3394 | case kVK_F15: return TK_PAUSE; 3395 | case kVK_ANSI_KeypadClear: return TK_NUMLOCK; 3396 | case kVK_ANSI_Semicolon: return TK_SEMICOLON; 3397 | case kVK_ANSI_Equal: return TK_EQUALS; 3398 | case kVK_ANSI_Comma: return TK_COMMA; 3399 | case kVK_ANSI_Minus: return TK_MINUS; 3400 | case kVK_ANSI_Slash: return TK_SLASH; 3401 | case kVK_ANSI_Backslash: return TK_BACKSLASH; 3402 | case kVK_ANSI_Grave: return TK_BACKTICK; 3403 | case kVK_ANSI_Quote: return TK_TICK; 3404 | case kVK_ANSI_LeftBracket: return TK_LSQUARE; 3405 | case kVK_ANSI_RightBracket: return TK_RSQUARE; 3406 | case kVK_ANSI_Period: return TK_DOT; 3407 | default: return 0; 3408 | } 3409 | } 3410 | 3411 | void _tigrOnCocoaEvent(id event, id window) 3412 | { 3413 | if(!event) 3414 | return; 3415 | 3416 | TigrInternal * win = _tigrInternalCocoa(window); 3417 | if(!win) // just pipe the event 3418 | { 3419 | objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), event); 3420 | return; 3421 | } 3422 | 3423 | NSUInteger eventType = ((NSUInteger (*)(id, SEL))objc_msgSend)(event, sel_registerName("type")); 3424 | switch(eventType) 3425 | { 3426 | case 1: // NSLeftMouseDown 3427 | win->mouseButtons |= 1; 3428 | break; 3429 | case 2: // NSLeftMouseUp 3430 | win->mouseButtons &= ~1; 3431 | break; 3432 | case 3: // NSRightMouseDown 3433 | win->mouseButtons |= 2; 3434 | break; 3435 | case 4: // NSRightMouseUp 3436 | win->mouseButtons &= ~2; 3437 | break; 3438 | case 25: // NSOtherMouseDown 3439 | { 3440 | // number == 2 is a middle button 3441 | NSInteger number = ((NSInteger (*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber")); 3442 | if(number == 2) 3443 | win->mouseButtons |= 4; 3444 | break; 3445 | } 3446 | case 26: // NSOtherMouseUp 3447 | { 3448 | NSInteger number = ((NSInteger (*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber")); 3449 | if(number == 2) 3450 | win->mouseButtons &= ~4; 3451 | break; 3452 | } 3453 | //case 22: // NSScrollWheel 3454 | //{ 3455 | // CGFloat deltaX = ((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("scrollingDeltaX")); 3456 | // CGFloat deltaY = ((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("scrollingDeltaY")); 3457 | // BOOL precisionScrolling = ((BOOL (*)(id, SEL))objc_msgSend)(event, sel_registerName("hasPreciseScrollingDeltas")); 3458 | // 3459 | // if(precisionScrolling) 3460 | // { 3461 | // deltaX *= 0.1f; // similar to glfw 3462 | // deltaY *= 0.1f; 3463 | // } 3464 | // 3465 | // if(fabs(deltaX) > 0.0f || fabs(deltaY) > 0.0f) 3466 | // printf("mouse scroll wheel delta %f %f\n", deltaX, deltaY); 3467 | // break; 3468 | //} 3469 | case 12: // NSFlagsChanged 3470 | { 3471 | NSUInteger modifiers = ((NSUInteger (*)(id, SEL))objc_msgSend)(event, sel_registerName("modifierFlags")); 3472 | 3473 | // based on NSEventModifierFlags and NSDeviceIndependentModifierFlagsMask 3474 | struct 3475 | { 3476 | union 3477 | { 3478 | struct 3479 | { 3480 | uint8_t alpha_shift:1; 3481 | uint8_t shift:1; 3482 | uint8_t control:1; 3483 | uint8_t alternate:1; 3484 | uint8_t command:1; 3485 | uint8_t numeric_pad:1; 3486 | uint8_t help:1; 3487 | uint8_t function:1; 3488 | }; 3489 | uint8_t mask; 3490 | }; 3491 | } keys; 3492 | 3493 | keys.mask = (modifiers & 0xffff0000UL) >> 16; 3494 | 3495 | // TODO L,R variation of keys? 3496 | win->keys[TK_CONTROL] = keys.alpha_shift; 3497 | win->keys[TK_SHIFT] = keys.shift; 3498 | win->keys[TK_CONTROL] = keys.control; 3499 | win->keys[TK_ALT] = keys.alternate; 3500 | win->keys[TK_LWIN] = keys.command; 3501 | win->keys[TK_RWIN] = keys.command; 3502 | break; 3503 | } 3504 | case 10: // NSKeyDown 3505 | { 3506 | id inputText = objc_msgSend_id(event, sel_registerName("characters")); 3507 | const char * inputTextUTF8 = ((const char* (*)(id, SEL))objc_msgSend)(inputText, sel_registerName("UTF8String")); 3508 | 3509 | tigrDecodeUTF8(inputTextUTF8, &win->lastChar); 3510 | 3511 | uint16_t keyCode = ((unsigned short (*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode")); 3512 | win->keys[_tigrKeyFromOSX(keyCode)] = 1; 3513 | break; 3514 | } 3515 | case 11: // NSKeyUp 3516 | { 3517 | uint16_t keyCode = ((unsigned short (*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode")); 3518 | win->keys[_tigrKeyFromOSX(keyCode)] = 0; 3519 | break; 3520 | } 3521 | default: 3522 | break; 3523 | } 3524 | 3525 | objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), event); 3526 | } 3527 | 3528 | void tigrUpdate(Tigr *bmp) 3529 | { 3530 | TigrInternal *win; 3531 | id openGLContext; 3532 | id window; 3533 | win = tigrInternal(bmp); 3534 | window = (id)bmp->handle; 3535 | openGLContext = (id)win->glContext; 3536 | 3537 | id keyWindow = objc_msgSend_id(NSApp, sel_registerName("keyWindow")); 3538 | 3539 | if(keyWindow == window) 3540 | memcpy(win->prev, win->keys, 256); 3541 | 3542 | id distantPast = objc_msgSend_id((id)objc_getClass("NSDate"), sel_registerName("distantPast")); 3543 | id event = ((id (*)(id, SEL, NSUInteger, id, id, BOOL))objc_msgSend)(NSApp, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), NSUIntegerMax, distantPast, NSDefaultRunLoopMode, YES); 3544 | _tigrOnCocoaEvent(event, keyWindow); 3545 | if(terminated || _tigrCocoaIsWindowClosed(window)) 3546 | return; 3547 | 3548 | // do runloop stuff 3549 | objc_msgSend_void(NSApp, sel_registerName("updateWindows")); 3550 | objc_msgSend_void(openGLContext, sel_registerName("update")); 3551 | objc_msgSend_void(openGLContext, sel_registerName("makeCurrentContext")); 3552 | 3553 | NSSize windowSize = _tigrCocoaWindowSize(window); 3554 | 3555 | if (win->flags & TIGR_AUTO) 3556 | tigrResize(bmp, windowSize.width / win->scale, windowSize.height / win->scale); 3557 | else 3558 | win->scale = tigrEnforceScale(tigrCalcScale(bmp->w, bmp->h, windowSize.width, windowSize.height), win->flags); 3559 | 3560 | tigrPosition(bmp, win->scale, windowSize.width, windowSize.height, win->pos); 3561 | tigrGAPIResize(bmp, windowSize.width, windowSize.height); 3562 | tigrGAPIPresent(bmp, windowSize.width, windowSize.height); 3563 | objc_msgSend_void(openGLContext, sel_registerName("flushBuffer")); 3564 | } 3565 | 3566 | int tigrClosed(Tigr *bmp) 3567 | { 3568 | return (terminated || _tigrCocoaIsWindowClosed((id)bmp->handle)) ? 1 : 0; 3569 | } 3570 | 3571 | void tigrMouse(Tigr *bmp, int *x, int *y, int *buttons) 3572 | { 3573 | TigrInternal *win; 3574 | id window; 3575 | win = tigrInternal(bmp); 3576 | window = (id)bmp->handle; 3577 | 3578 | id windowContentView = objc_msgSend_id(window, sel_registerName("contentView")); 3579 | NSRect adjustFrame = ((NSRect (*)(id, SEL))abi_objc_msgSend_stret)(windowContentView, sel_registerName("frame")); 3580 | 3581 | // NSPoint is small enough to fit a register, so no need for objc_msgSend_stret 3582 | NSPoint p = ((NSPoint (*)(id, SEL))objc_msgSend)(window, sel_registerName("mouseLocationOutsideOfEventStream")); 3583 | 3584 | // map input to content view rect 3585 | if(p.x < 0) p.x = 0; 3586 | else if(p.x > adjustFrame.size.width) p.x = adjustFrame.size.width; 3587 | if(p.y < 0) p.y = 0; 3588 | else if(p.y > adjustFrame.size.height) p.y = adjustFrame.size.height; 3589 | 3590 | // map input to pixels 3591 | NSRect r = {p.x, p.y, 0, 0}; 3592 | r = ((NSRect (*)(id, SEL, NSRect))abi_objc_msgSend_stret)(windowContentView, sel_registerName("convertRectToBacking:"), r); 3593 | p = r.origin; 3594 | 3595 | p.x = (p.x - win->pos[0]) / win->scale; 3596 | p.y = (adjustFrame.size.height - p.y - win->pos[1]) / win->scale; 3597 | 3598 | if(x) 3599 | *x = p.x; 3600 | if(y) 3601 | *y = p.y; 3602 | 3603 | if(buttons) 3604 | { 3605 | id keyWindow = objc_msgSend_id(NSApp, sel_registerName("keyWindow")); 3606 | *buttons = keyWindow != bmp->handle ? 0 : win->mouseButtons; 3607 | } 3608 | } 3609 | 3610 | int tigrKeyDown(Tigr *bmp, int key) 3611 | { 3612 | TigrInternal *win; 3613 | assert(key < 256); 3614 | id keyWindow = objc_msgSend_id(NSApp, sel_registerName("keyWindow")); 3615 | if(keyWindow != bmp->handle) 3616 | return 0; 3617 | win = tigrInternal(bmp); 3618 | return win->keys[key] && !win->prev[key]; 3619 | } 3620 | 3621 | int tigrKeyHeld(Tigr *bmp, int key) 3622 | { 3623 | TigrInternal *win; 3624 | assert(key < 256); 3625 | id keyWindow = objc_msgSend_id(NSApp, sel_registerName("keyWindow")); 3626 | if(keyWindow != bmp->handle) 3627 | return 0; 3628 | win = tigrInternal(bmp); 3629 | return win->keys[key]; 3630 | } 3631 | 3632 | int tigrReadChar(Tigr *bmp) 3633 | { 3634 | TigrInternal *win = tigrInternal(bmp); 3635 | int c = win->lastChar; 3636 | win->lastChar = 0; 3637 | return c; 3638 | } 3639 | 3640 | float tigrTime() 3641 | { 3642 | static uint64_t time = 0; 3643 | static mach_timebase_info_data_t timebaseInfo; 3644 | 3645 | if(timebaseInfo.denom == 0) 3646 | { 3647 | mach_timebase_info(&timebaseInfo); 3648 | time = mach_absolute_time(); 3649 | return 0.0f; 3650 | } 3651 | 3652 | uint64_t current_time = mach_absolute_time(); 3653 | double elapsed = (double)(current_time - time) * timebaseInfo.numer / (timebaseInfo.denom * 1000000000.0); 3654 | time = current_time; 3655 | return (float)elapsed; 3656 | } 3657 | 3658 | #endif 3659 | #endif 3660 | 3661 | //////// End of inlined file: tigr_osx.c //////// 3662 | 3663 | //////// Start of inlined file: tigr_gl.c //////// 3664 | 3665 | //#include "tigr_internal.h" 3666 | #include // TODO can we remove this and printf's later? 3667 | 3668 | #ifdef TIGR_GAPI_GL 3669 | #ifndef __APPLE__ 3670 | // please provide you own glext.h, you can download latest at https://www.opengl.org/registry/api/GL/glext.h 3671 | #include 3672 | #endif 3673 | #ifdef _WIN32 3674 | // please provide you own wglext.h, you can download latest at https://www.opengl.org/registry/api/GL/wglext.h 3675 | #include 3676 | #endif 3677 | 3678 | extern const unsigned char tigr_upscale_gl_vs[], tigr_upscale_gl_fs[]; 3679 | extern int tigr_upscale_gl_vs_size, tigr_upscale_gl_fs_size; 3680 | 3681 | #ifdef _WIN32 3682 | PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat; 3683 | PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs; 3684 | PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; 3685 | PFNGLGENBUFFERSARBPROC glGenBuffers; 3686 | PFNGLBINDBUFFERPROC glBindBuffer; 3687 | PFNGLBUFFERDATAPROC glBufferData; 3688 | PFNGLBINDVERTEXARRAYPROC glBindVertexArray; 3689 | PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; 3690 | PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; 3691 | PFNGLCREATESHADERPROC glCreateShader; 3692 | PFNGLSHADERSOURCEPROC glShaderSource; 3693 | PFNGLCOMPILESHADERPROC glCompileShader; 3694 | PFNGLCREATEPROGRAMPROC glCreateProgram; 3695 | PFNGLATTACHSHADERPROC glAttachShader; 3696 | PFNGLLINKPROGRAMPROC glLinkProgram; 3697 | PFNGLDELETESHADERPROC glDeleteShader; 3698 | PFNGLDELETEPROGRAMPROC glDeleteProgram; 3699 | PFNGLGETSHADERIVPROC glGetShaderiv; 3700 | PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; 3701 | PFNGLGETPROGRAMIVPROC glGetProgramiv; 3702 | PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; 3703 | PFNGLUSEPROGRAMPROC glUseProgram; 3704 | PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; 3705 | PFNGLUNIFORM4FPROC glUniform4f; 3706 | PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; 3707 | PFNGLACTIVETEXTUREPROC glActiveTexture; 3708 | int tigrGL11Init(Tigr *bmp) 3709 | { 3710 | int pixel_format; 3711 | TigrInternal *win = tigrInternal(bmp); 3712 | GLStuff *gl= &win->gl; 3713 | PIXELFORMATDESCRIPTOR pfd = 3714 | { 3715 | sizeof(PIXELFORMATDESCRIPTOR), 3716 | 1, 3717 | PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_SWAP_EXCHANGE, 3718 | PFD_TYPE_RGBA, 3719 | 32, // color bits 3720 | 0, 0, 0, 0, 0, 0, 0, 0, 3721 | 0, 0, 0, 0, 0, 3722 | 24, // depth 3723 | 8, // stencil 3724 | 0, 3725 | PFD_MAIN_PLANE, // is it ignored ? 3726 | 0, 3727 | 0, 0, 0 3728 | }; 3729 | if(!(gl->dc = GetDC((HWND)bmp->handle))) {tigrError(bmp, "Cannot create OpenGL device context.\n"); return -1;} 3730 | if(!(pixel_format = ChoosePixelFormat(gl->dc, &pfd))) {tigrError(bmp, "Cannot choose OpenGL pixel format.\n"); return -1;} 3731 | if(!SetPixelFormat(gl->dc, pixel_format, &pfd)) {tigrError(bmp, "Cannot set OpenGL pixel format.\n"); return -1;} 3732 | if(!(gl->hglrc = wglCreateContext(gl->dc))) {tigrError(bmp, "Cannot create OpenGL context.\n"); return -1;} 3733 | if(!wglMakeCurrent(gl->dc, gl->hglrc)) {tigrError(bmp, "Cannot activate OpenGL context.\n"); return -1;} 3734 | gl->gl_legacy = 1; 3735 | return 0; 3736 | } 3737 | int tigrGL33Init(Tigr *bmp) 3738 | { 3739 | int pixel_format; 3740 | UINT num_formats; 3741 | TigrInternal *win = tigrInternal(bmp); 3742 | GLStuff *gl= &win->gl; 3743 | 3744 | wglChoosePixelFormat = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); 3745 | wglCreateContextAttribs = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB"); 3746 | glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)wglGetProcAddress("glGenVertexArrays"); 3747 | glGenBuffers = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffers"); 3748 | glBindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer"); 3749 | glBufferData = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData"); 3750 | glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)wglGetProcAddress("glBindVertexArray"); 3751 | glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glEnableVertexAttribArray"); 3752 | glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer"); 3753 | glCreateShader = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader"); 3754 | glShaderSource = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource"); 3755 | glCompileShader = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader"); 3756 | glCreateProgram = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram"); 3757 | glAttachShader = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader"); 3758 | glLinkProgram = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram"); 3759 | glDeleteShader = (PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader"); 3760 | glDeleteProgram = (PFNGLDELETEPROGRAMPROC)wglGetProcAddress("glDeleteProgram"); 3761 | glGetShaderiv = (PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv"); 3762 | glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog"); 3763 | glGetProgramiv = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv"); 3764 | glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog"); 3765 | glUseProgram = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"); 3766 | glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)wglGetProcAddress("glGetUniformLocation"); 3767 | glUniform4f = (PFNGLUNIFORM4FPROC)wglGetProcAddress("glUniform4f"); 3768 | glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)wglGetProcAddress("glUniformMatrix4fv"); 3769 | glActiveTexture = (PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTexture"); 3770 | 3771 | if(!wglChoosePixelFormat || !wglCreateContextAttribs) {tigrError(bmp, "Cannot create OpenGL context.\n"); return -1;} 3772 | const int attribList[] = 3773 | { 3774 | WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, 3775 | WGL_SUPPORT_OPENGL_ARB, GL_TRUE, 3776 | WGL_DOUBLE_BUFFER_ARB, GL_TRUE, 3777 | WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, 3778 | WGL_COLOR_BITS_ARB, 32, 3779 | WGL_DEPTH_BITS_ARB, 24, 3780 | WGL_STENCIL_BITS_ARB, 8, 3781 | 0 3782 | }; 3783 | int attribs[] = { 3784 | WGL_CONTEXT_MAJOR_VERSION_ARB, 3, 3785 | WGL_CONTEXT_MINOR_VERSION_ARB, 3, 3786 | 0 3787 | }; 3788 | if(!wglChoosePixelFormat(gl->dc, attribList, NULL, 1, &pixel_format, &num_formats)) {tigrError(bmp, "Cannot choose OpenGL pixel format.\n"); return -1;} 3789 | if(!(gl->hglrc = wglCreateContextAttribs(gl->dc, gl->hglrc, attribs))) {tigrError(bmp, "Cannot create OpenGL context attribs.\n"); return -1;} 3790 | if(!wglMakeCurrent(gl->dc, gl->hglrc)) {tigrError(bmp, "Cannot activate OpenGL context.\n"); return -1;} 3791 | gl->gl_legacy = 0; 3792 | return 0; 3793 | } 3794 | #endif 3795 | 3796 | void tigrCheckGLError(const char *state) 3797 | { 3798 | GLenum err = glGetError(); 3799 | if(err != GL_NO_ERROR) 3800 | printf("got gl error %x when was doing %s\n", err, state); 3801 | } 3802 | 3803 | void tigrCheckShaderErrors(GLuint object) 3804 | { 3805 | GLint success; 3806 | GLchar info[2048]; 3807 | glGetShaderiv(object, GL_COMPILE_STATUS, &success); 3808 | if(!success) 3809 | { 3810 | glGetShaderInfoLog(object, sizeof(info), NULL, info); 3811 | printf("shader compile error : %s\n", info); 3812 | } 3813 | } 3814 | 3815 | void tigrCheckProgramErrors(GLuint object) 3816 | { 3817 | GLint success; 3818 | GLchar info[2048]; 3819 | glGetProgramiv(object, GL_LINK_STATUS, &success); 3820 | if(!success) 3821 | { 3822 | glGetProgramInfoLog(object, sizeof(info), NULL, info); 3823 | printf("shader link error : %s\n", info); 3824 | } 3825 | } 3826 | 3827 | void tigrGAPICreate(Tigr *bmp) 3828 | { 3829 | GLuint vs, fs; 3830 | TigrInternal *win = tigrInternal(bmp); 3831 | GLStuff *gl= &win->gl; 3832 | GLuint VBO; 3833 | GLfloat vertices[] = { 3834 | // pos uv 3835 | 0.0f, 1.0f, 0.0f, 1.0f, 3836 | 1.0f, 0.0f, 1.0f, 0.0f, 3837 | 0.0f, 0.0f, 0.0f, 0.0f, 3838 | 0.0f, 1.0f, 0.0f, 1.0f, 3839 | 1.0f, 1.0f, 1.0f, 1.0f, 3840 | 1.0f, 0.0f, 1.0f, 0.0f 3841 | }; 3842 | 3843 | #ifdef _WIN32 3844 | if(tigrGL11Init(bmp)) 3845 | return; 3846 | tigrGL33Init(bmp); 3847 | #endif 3848 | 3849 | //printf("ogl version %s\n", glGetString(GL_VERSION)); 3850 | 3851 | if(!gl->gl_legacy) 3852 | { 3853 | // create vao 3854 | glGenVertexArrays(1, &gl->vao); 3855 | glGenBuffers(1, &VBO); 3856 | glBindBuffer(GL_ARRAY_BUFFER, VBO); 3857 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 3858 | glBindVertexArray(gl->vao); 3859 | glEnableVertexAttribArray(0); 3860 | glEnableVertexAttribArray(1); 3861 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), NULL); 3862 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), NULL); 3863 | 3864 | // create program 3865 | vs = glCreateShader(GL_VERTEX_SHADER); 3866 | const char *vs_source = (const char*)&tigr_upscale_gl_vs; 3867 | glShaderSource(vs, 1, &vs_source, &tigr_upscale_gl_vs_size); 3868 | glCompileShader(vs); 3869 | tigrCheckShaderErrors(vs); 3870 | fs = glCreateShader(GL_FRAGMENT_SHADER); 3871 | const char *fs_source = (const char*)&tigr_upscale_gl_fs; 3872 | glShaderSource(fs, 1, &fs_source, &tigr_upscale_gl_fs_size); 3873 | glCompileShader(fs); 3874 | tigrCheckShaderErrors(fs); 3875 | gl->program = glCreateProgram(); 3876 | glAttachShader(gl->program, vs); 3877 | glAttachShader(gl->program, fs); 3878 | glLinkProgram(gl->program); 3879 | tigrCheckProgramErrors(gl->program); 3880 | glDeleteShader(vs); 3881 | glDeleteShader(fs); 3882 | gl->uniform_projection = glGetUniformLocation(gl->program, "projection"); 3883 | gl->uniform_model = glGetUniformLocation(gl->program, "model"); 3884 | gl->uniform_parameters = glGetUniformLocation(gl->program, "parameters"); 3885 | } 3886 | 3887 | // create textures 3888 | if(gl->gl_legacy) 3889 | glEnable(GL_TEXTURE_2D); 3890 | glGenTextures(2, gl->tex); 3891 | for(int i = 0; i < 2; ++i) { 3892 | glBindTexture(GL_TEXTURE_2D, gl->tex[i]); 3893 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->gl_legacy ? GL_NEAREST : GL_LINEAR); 3894 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->gl_legacy ? GL_NEAREST : GL_LINEAR); 3895 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 3896 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 3897 | } 3898 | 3899 | tigrCheckGLError("initialization"); 3900 | } 3901 | 3902 | void tigrGAPIBegin(Tigr *bmp) 3903 | { 3904 | } 3905 | 3906 | void tigrGAPIEnd(Tigr *bmp) 3907 | { 3908 | } 3909 | 3910 | void tigrGAPIDestroy(Tigr *bmp) 3911 | { 3912 | TigrInternal *win = tigrInternal(bmp); 3913 | GLStuff *gl= &win->gl; 3914 | 3915 | if(!gl->gl_legacy) 3916 | { 3917 | glDeleteTextures(2, gl->tex); 3918 | glDeleteProgram(gl->program); 3919 | } 3920 | 3921 | tigrCheckGLError("destroy"); 3922 | 3923 | #ifdef _WIN32 3924 | if(!wglMakeCurrent(NULL, NULL)) {tigrError(bmp, "Cannot deactivate OpenGL context.\n"); return;} 3925 | if(gl->hglrc && !wglDeleteContext(gl->hglrc)) {tigrError(bmp, "Cannot delete OpenGL context.\n"); return;} 3926 | gl->hglrc = NULL; 3927 | 3928 | if(gl->dc && !ReleaseDC((HWND)bmp->handle, gl->dc)) {tigrError(bmp, "Cannot release OpenGL device context.\n"); return;} 3929 | gl->dc = NULL; 3930 | #endif 3931 | } 3932 | 3933 | void tigrGAPIResize(Tigr *bmp, int width, int height) 3934 | { 3935 | // no-op 3936 | (void)bmp; 3937 | (void)width; 3938 | (void)height; 3939 | } 3940 | 3941 | void tigrGAPIDraw(int legacy, GLuint uniform_model, GLuint tex, Tigr *bmp, int x1, int y1, int x2, int y2) 3942 | { 3943 | glBindTexture(GL_TEXTURE_2D, tex); 3944 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, bmp->w, bmp->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, bmp->pix); 3945 | 3946 | if(!legacy) 3947 | { 3948 | float sx = (float)(x2 - x1); 3949 | float sy = (float)(y2 - y1); 3950 | float tx = (float)x1; 3951 | float ty = (float)y1; 3952 | 3953 | float model[16] = 3954 | { 3955 | sx, 0.0f, 0.0f, 0.0f, 3956 | 0.0f, sy, 0.0f, 0.0f, 3957 | 0.0f, 0.0f, 1.0f, 0.0f, 3958 | tx, ty, 0.0f, 1.0f 3959 | }; 3960 | 3961 | glUniformMatrix4fv(uniform_model, 1, GL_FALSE, model); 3962 | glDrawArrays(GL_TRIANGLES, 0, 6); 3963 | } 3964 | else 3965 | { 3966 | #ifndef __APPLE__ 3967 | glBegin(GL_QUADS); 3968 | glTexCoord2f(1.0f, 0.0f); glVertex2i(x2, y1); 3969 | glTexCoord2f(0.0f, 0.0f); glVertex2i(x1, y1); 3970 | glTexCoord2f(0.0f, 1.0f); glVertex2i(x1, y2); 3971 | glTexCoord2f(1.0f, 1.0f); glVertex2i(x2, y2); 3972 | glEnd(); 3973 | #else 3974 | assert(0); 3975 | #endif 3976 | } 3977 | } 3978 | 3979 | void tigrGAPIPresent(Tigr *bmp, int w, int h) 3980 | { 3981 | TigrInternal *win = tigrInternal(bmp); 3982 | GLStuff *gl= &win->gl; 3983 | 3984 | #ifdef _WIN32 3985 | wglMakeCurrent(gl->dc, gl->hglrc); 3986 | #endif 3987 | glViewport(0, 0, w, h); 3988 | glClearColor(0, 0, 0, 1); 3989 | glClear(GL_COLOR_BUFFER_BIT); 3990 | 3991 | if(!gl->gl_legacy) 3992 | { 3993 | float projection[16] = 3994 | { 3995 | 2.0f / w, 0.0f , 0.0f, 0.0f, 3996 | 0.0f , -2.0f / h, 0.0f, 0.0f, 3997 | 0.0f , 0.0f , 1.0f, 0.0f, 3998 | -1.0f , 1.0f , 0.0f, 1.0f 3999 | }; 4000 | 4001 | glActiveTexture(GL_TEXTURE0); 4002 | glBindVertexArray(gl->vao); 4003 | glUseProgram(gl->program); 4004 | glUniformMatrix4fv(gl->uniform_projection, 1, GL_FALSE, projection); 4005 | glUniform4f(gl->uniform_parameters, win->hblur ? 1.0f : 0.0f, win->vblur ? 1.0f : 0.0f, win->scanlines, win->contrast); 4006 | } 4007 | else 4008 | { 4009 | #ifndef __APPLE__ 4010 | glMatrixMode(GL_PROJECTION); 4011 | glLoadIdentity(); 4012 | glOrtho(0, w, h, 0, -1.0f, 1.0f); 4013 | glEnable(GL_TEXTURE_2D); 4014 | #else 4015 | assert(0); 4016 | #endif 4017 | } 4018 | 4019 | glDisable(GL_BLEND); 4020 | tigrGAPIDraw(gl->gl_legacy, gl->uniform_model, gl->tex[0], bmp, win->pos[0], win->pos[1], win->pos[2], win->pos[3]); 4021 | 4022 | if(win->widgetsScale > 0) 4023 | { 4024 | glEnable(GL_BLEND); 4025 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 4026 | tigrGAPIDraw(gl->gl_legacy, gl->uniform_model, gl->tex[1], win->widgets, 4027 | (int)(w - win->widgets->w * win->widgetsScale), 0, 4028 | w, (int)(win->widgets->h * win->widgetsScale)); 4029 | } 4030 | 4031 | tigrCheckGLError("present"); 4032 | 4033 | #ifdef _WIN32 4034 | if(!SwapBuffers(gl->dc)) {tigrError(bmp, "Cannot swap OpenGL buffers.\n"); return;} 4035 | #endif 4036 | } 4037 | 4038 | #endif 4039 | 4040 | //////// End of inlined file: tigr_gl.c //////// 4041 | 4042 | 4043 | //////// End of inlined file: tigr_amalgamated.c //////// 4044 | 4045 | -------------------------------------------------------------------------------- /demo/tigr.h: -------------------------------------------------------------------------------- 1 | // TIGR - TIny GRaphics Library - v1.3 2 | // ^^ ^^ 3 | // 4 | // rawr. 5 | 6 | /* 7 | This is free and unencumbered software released into the public domain. 8 | 9 | Our intent is that anyone is free to copy and use this software, 10 | for any purpose, in any form, and by any means. 11 | 12 | The authors dedicate any and all copyright interest in the software 13 | to the public domain, at their own expense for the betterment of mankind. 14 | 15 | The software is provided "as is", without any kind of warranty, including 16 | any implied warranty. If it breaks, you get to keep both pieces. 17 | */ 18 | 19 | #pragma once 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | // Graphics configuration. 26 | #ifdef _WIN32 27 | #define TIGR_GAPI_D3D9 28 | #else 29 | #define TIGR_GAPI_GL 30 | #endif 31 | 32 | // Compiler configuration. 33 | #ifdef _MSC_VER 34 | #define TIGR_INLINE static __forceinline 35 | #else 36 | #define TIGR_INLINE static inline 37 | #endif 38 | 39 | // Bitmaps ---------------------------------------------------------------- 40 | 41 | // This struct contains one pixel. 42 | typedef struct { 43 | unsigned char b, g, r, a; 44 | } TPixel; 45 | 46 | // Windows flags. 47 | #define TIGR_FIXED 0 // window's bitmap is a fixed size (default) 48 | #define TIGR_AUTO 1 // window's bitmap will automatically resize after each tigrUpdate 49 | #define TIGR_2X 2 // always enforce (at least) 2X pixel scale 50 | #define TIGR_3X 4 // always enforce (at least) 3X pixel scale 51 | #define TIGR_4X 8 // always enforce (at least) 4X pixel scale 52 | #define TIGR_RETINA 16 // enable retina support on OS X 53 | 54 | // A Tigr bitmap. 55 | typedef struct Tigr { 56 | int w, h; // width/height (unscaled) 57 | TPixel *pix; // pixel data 58 | void *handle; // OS window handle, NULL for off-screen bitmaps. 59 | } Tigr; 60 | 61 | // Creates a new empty window. (title is UTF-8) 62 | Tigr *tigrWindow(int w, int h, const char *title, int flags); 63 | 64 | // Creates an empty off-screen bitmap. 65 | Tigr *tigrBitmap(int w, int h); 66 | 67 | // Deletes a window/bitmap. 68 | void tigrFree(Tigr *bmp); 69 | 70 | // Returns non-zero if the user requested to close a window. 71 | int tigrClosed(Tigr *bmp); 72 | 73 | // Displays a window's contents on-screen. 74 | void tigrUpdate(Tigr *bmp); 75 | 76 | // Sets post-FX properties for a window. 77 | // hblur/vblur = whether to use bilinear filtering along that axis (boolean) 78 | // scanlines = CRT scanlines effect (0-1) 79 | // contrast = contrast boost (1 = no change, 2 = 2X contrast, etc) 80 | void tigrSetPostFX(Tigr *bmp, int hblur, int vblur, float scanlines, float contrast); 81 | 82 | 83 | // Drawing ---------------------------------------------------------------- 84 | 85 | // Helper for reading/writing pixels. 86 | // For high performance, just write to bmp->pix yourself. 87 | TPixel tigrGet(Tigr *bmp, int x, int y); 88 | void tigrPlot(Tigr *bmp, int x, int y, TPixel pix); 89 | 90 | // Clears a bitmap to a color. 91 | void tigrClear(Tigr *bmp, TPixel color); 92 | 93 | // Fills in a solid rectangle. 94 | void tigrFill(Tigr *bmp, int x, int y, int w, int h, TPixel color); 95 | 96 | // Draws an empty rectangle. (exclusive co-ords) 97 | void tigrRect(Tigr *bmp, int x, int y, int w, int h, TPixel color); 98 | 99 | // Draws a line. 100 | void tigrLine(Tigr *bmp, int x0, int y0, int x1, int y1, TPixel color); 101 | 102 | // Copies bitmap data. 103 | // dx/dy = dest co-ordinates 104 | // sx/sy = source co-ordinates 105 | // w/h = width/height 106 | void tigrBlit(Tigr *dest, Tigr *src, int dx, int dy, int sx, int sy, int w, int h); 107 | 108 | // Same as tigrBlit, but blends with the bitmap alpha channel, 109 | // and uses the 'alpha' variable to fade out. 110 | void tigrBlitAlpha(Tigr *dest, Tigr *src, int dx, int dy, int sx, int sy, int w, int h, float alpha); 111 | 112 | // Same as tigrBlit, but tints the source bitmap with a color. 113 | void tigrBlitTint(Tigr *dest, Tigr *src, int dx, int dy, int sx, int sy, int w, int h, TPixel tint); 114 | 115 | // Helper for making colors. 116 | TIGR_INLINE TPixel tigrRGB(unsigned char r, unsigned char g, unsigned char b) 117 | { 118 | TPixel p; p.r = r; p.g = g; p.b = b; p.a = 0xff; return p; 119 | } 120 | 121 | // Helper for making colors. 122 | TIGR_INLINE TPixel tigrRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) 123 | { 124 | TPixel p; p.r = r; p.g = g; p.b = b; p.a = a; return p; 125 | } 126 | 127 | 128 | // Font printing ---------------------------------------------------------- 129 | 130 | typedef struct { 131 | int code, x, y, w, h; 132 | } TigrGlyph; 133 | 134 | typedef struct { 135 | Tigr *bitmap; 136 | int numGlyphs; 137 | TigrGlyph *glyphs; 138 | } TigrFont; 139 | 140 | // Loads a font. The font bitmap should contain all characters 141 | // for the given codepage, excluding the first 32 control codes. 142 | // Supported codepages: 143 | // 0 - Regular 7-bit ASCII 144 | // 1252 - Windows 1252 145 | TigrFont *tigrLoadFont(Tigr *bitmap, int codepage); 146 | 147 | // Frees a font. 148 | void tigrFreeFont(TigrFont *font); 149 | 150 | // Prints UTF-8 text onto a bitmap. 151 | void tigrPrint(Tigr *dest, TigrFont *font, int x, int y, TPixel color, const char *text, ...); 152 | 153 | // Returns the width/height of a string. 154 | int tigrTextWidth(TigrFont *font, const char *text); 155 | int tigrTextHeight(TigrFont *font, const char *text); 156 | 157 | // The built-in font. 158 | extern TigrFont *tfont; 159 | 160 | 161 | // User Input ------------------------------------------------------------- 162 | 163 | // Key scancodes. For letters/numbers, use ASCII ('A'-'Z' and '0'-'9'). 164 | typedef enum { 165 | TK_PAD0=128,TK_PAD1,TK_PAD2,TK_PAD3,TK_PAD4,TK_PAD5,TK_PAD6,TK_PAD7,TK_PAD8,TK_PAD9, 166 | TK_PADMUL,TK_PADADD,TK_PADENTER,TK_PADSUB,TK_PADDOT,TK_PADDIV, 167 | TK_F1,TK_F2,TK_F3,TK_F4,TK_F5,TK_F6,TK_F7,TK_F8,TK_F9,TK_F10,TK_F11,TK_F12, 168 | TK_BACKSPACE,TK_TAB,TK_RETURN,TK_SHIFT,TK_CONTROL,TK_ALT,TK_PAUSE,TK_CAPSLOCK, 169 | TK_ESCAPE,TK_SPACE,TK_PAGEUP,TK_PAGEDN,TK_END,TK_HOME,TK_LEFT,TK_UP,TK_RIGHT,TK_DOWN, 170 | TK_INSERT,TK_DELETE,TK_LWIN,TK_RWIN,TK_NUMLOCK,TK_SCROLL,TK_LSHIFT,TK_RSHIFT, 171 | TK_LCONTROL,TK_RCONTROL,TK_LALT,TK_RALT,TK_SEMICOLON,TK_EQUALS,TK_COMMA,TK_MINUS, 172 | TK_DOT,TK_SLASH,TK_BACKTICK,TK_LSQUARE,TK_BACKSLASH,TK_RSQUARE,TK_TICK 173 | } TKey; 174 | 175 | // Returns mouse input for a window. 176 | void tigrMouse(Tigr *bmp, int *x, int *y, int *buttons); 177 | 178 | // Reads the keyboard for a window. 179 | // Returns non-zero if a key is pressed/held. 180 | // tigrKeyDown tests for the initial press, tigrKeyHeld repeats each frame. 181 | int tigrKeyDown(Tigr *bmp, int key); 182 | int tigrKeyHeld(Tigr *bmp, int key); 183 | 184 | // Reads character input for a window. 185 | // Returns the Unicode value of the last key pressed, or 0 if none. 186 | int tigrReadChar(Tigr *bmp); 187 | 188 | 189 | // Bitmap I/O ------------------------------------------------------------- 190 | 191 | // Loads a PNG, from either a file or memory. (fileName is UTF-8) 192 | // On error, returns NULL and sets errno. 193 | Tigr *tigrLoadImage(const char *fileName); 194 | Tigr *tigrLoadImageMem(const void *data, int length); 195 | 196 | // Saves a PNG to a file. (fileName is UTF-8) 197 | // On error, returns zero and sets errno. 198 | int tigrSaveImage(const char *fileName, Tigr *bmp); 199 | 200 | 201 | // Helpers ---------------------------------------------------------------- 202 | 203 | // Returns the amount of time elapsed since tigrTime was last called, 204 | // or zero on the first call. 205 | float tigrTime(); 206 | 207 | // Displays an error message and quits. (UTF-8) 208 | // 'bmp' can be NULL. 209 | void tigrError(Tigr *bmp, const char *message, ...); 210 | 211 | // Reads an entire file into memory. (fileName is UTF-8) 212 | // Free it yourself after with 'free'. 213 | // On error, returns NULL and sets errno. 214 | // TIGR will automatically append a NUL terminator byte 215 | // to the end (not included in the length) 216 | void *tigrReadFile(const char *fileName, int *length); 217 | 218 | // Decompresses DEFLATEd zip/zlib data into a buffer. 219 | // Returns non-zero on success. 220 | int tigrInflate(void *out, unsigned outlen, const void *in, unsigned inlen); 221 | 222 | // Decodes a single UTF8 codepoint and returns the next pointer. 223 | const char *tigrDecodeUTF8(const char *text, int *cp); 224 | 225 | // Encodes a single UTF8 codepoint and returns the next pointer. 226 | char *tigrEncodeUTF8(char *text, int cp); 227 | 228 | #ifdef __cplusplus 229 | } 230 | #endif 231 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Dmytro Ivanov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /nativefonts.h: -------------------------------------------------------------------------------- 1 | // nativefonts 2 | // MIT license 3 | // 4 | // codestyle : 5 | // - C99 6 | // - 80 columns max 7 | // - tab = 4 spaces 8 | // - snake_case 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | // --------------------------------------------------------------- configuration 21 | 22 | // todo proper config 23 | #ifdef _WIN32 24 | #define NF_PLATFORM_DWRITE 25 | #endif 26 | #ifdef __APPLE__ 27 | #define NF_PLATFORM_CORETEXT 28 | #endif 29 | 30 | #ifndef NF_MAX_WIDTH 31 | #define NF_MAX_WIDTH 2048 32 | #endif 33 | 34 | #ifndef NF_MAX_HEIGHT 35 | #define NF_MAX_HEIGHT 2048 36 | #endif 37 | 38 | #ifndef NF_ERROR 39 | #define NF_ERROR(msg, ...) fprintf(stderr, msg, __VA_ARGS__) 40 | #endif 41 | 42 | // ------------------------------------------------------------- font parameters 43 | typedef enum { 44 | NF_WEIGHT_NORMAL = 0, 45 | NF_WEIGHT_BOLD, 46 | NF_WEIGHT_LIGHT, 47 | NF_WEIGHT_MAX = 0xff 48 | } nf_font_weight_t; 49 | 50 | typedef enum { 51 | NF_STYLE_NORMAL = 0, 52 | NF_STYLE_OBLIQUE, 53 | NF_STYLE_ITALIC, 54 | NF_STYLE_MAX = 0xff 55 | } nf_font_style_t; 56 | 57 | typedef enum { 58 | NF_STRETCH_NORMAL = 0, 59 | NF_STRETCH_EXPANDED, 60 | NF_STRETCH_CONDENSED, 61 | NF_STRETCH_MAX = 0xff 62 | } nf_font_stretch_t; 63 | 64 | typedef struct { 65 | float size_in_pt; 66 | nf_font_weight_t weight; 67 | nf_font_style_t style; 68 | nf_font_stretch_t stretch; 69 | } nf_font_params_t; 70 | 71 | // ---------------------------------------------------------- system information 72 | typedef enum { 73 | NF_BITMAP_B8G8R8A8_UNORM_PMA = 0, 74 | } nf_bitmap_t; 75 | 76 | typedef struct { 77 | nf_bitmap_t bitmap; 78 | uint16_t max_width; 79 | uint16_t max_height; 80 | float ppi_x; 81 | float ppi_y; 82 | } nf_system_info_t; 83 | 84 | // --------------------------------------------------------------- text features 85 | typedef enum { 86 | // emphasis 87 | NF_FEATURE_BOLD = 0, 88 | NF_FEATURE_UNDERLINE, 89 | NF_FEATURE_ITALIC, 90 | 91 | // TODO more effects 92 | 93 | // wrapping, by default text is wrapped 94 | NF_FEATURE_WRAP, 95 | NF_FEATURE_NO_WRAP, 96 | 97 | // align features are applied to whole text 98 | // defaults are left, left 99 | NF_FEATURE_ALIGN_LEFT, 100 | NF_FEATURE_ALIGN_CENTER, 101 | NF_FEATURE_ALIGN_RIGHT, 102 | NF_FEATURE_ALIGN_JUSTIFIED, 103 | NF_FEATURE_ALIGN_PARAGRAPH_LEFT, 104 | NF_FEATURE_ALIGN_PARAGRAPH_CENTER, 105 | NF_FEATURE_ALIGN_PARAGRAPH_RIGHT, 106 | 107 | // antialiasing is applied to whole text 108 | NF_FEATURE_AA_DISABLED, 109 | // default on Windows, greyscale uses BGRA as premultiplied alpha 110 | NF_FEATURE_AA_WIN_GREYSCALE, 111 | // cleartype will not use alpha channel, so treat bitmap as BGR 112 | NF_FEATURE_AA_WIN_CLEARTYPE, 113 | 114 | // ppi is applied to whole text 115 | // default is system defined, see system info 116 | NF_FEATURE_PPI, 117 | 118 | // color is applied to whole text (TODO?) 119 | NF_FEATURE_COLOR_BG, // default is 0,0,0,0 120 | NF_FEATURE_COLOR_TEXT, // default is 1,1,1,1 121 | 122 | NF_FEATURE_MAX = 0xff 123 | } nf_feature_type_t; 124 | 125 | typedef struct { 126 | nf_feature_type_t type; 127 | union 128 | { 129 | struct { size_t start, end; } range; 130 | struct { float r, g, b, a; } color; 131 | struct { float x, y; } ppi; 132 | }; 133 | } nf_feature_t; 134 | 135 | // ---------------------------------------------------------------- bounding box 136 | 137 | typedef struct { 138 | uint16_t x; 139 | uint16_t y; 140 | uint16_t w; 141 | uint16_t h; 142 | } nf_aabb_t; 143 | 144 | // ------------------------------------------------------------------- functions 145 | typedef uintptr_t nf_font_t; 146 | 147 | nf_system_info_t nf_system_info(); 148 | 149 | // returns 0 on error 150 | nf_font_t nf_font(const char * font_name, nf_font_params_t params); 151 | void nf_free(nf_font_t font); 152 | 153 | // return < 0 on error 154 | int nf_print( 155 | void * bitmap, uint16_t w, uint16_t h, 156 | nf_font_t font, 157 | nf_feature_t * features, size_t features_count, 158 | nf_aabb_t * result_rect, 159 | const char * text); 160 | 161 | #ifdef __cplusplus 162 | } 163 | #endif 164 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # nativefonts 2 | 3 | A simple, MIT licensed, way to use native font rendering. 4 | 5 | ### Native backends 6 | 7 | Currently we support : 8 | 9 | - DirectWrite 10 | - Core Text 11 | 12 | ### API 13 | 14 | The API contains only 4 functions : everything you need to do simple text-to-bitmap rendering. 15 | 16 | ```C 17 | nf_system_info_t nf_system_info(); 18 | 19 | // returns 0 on error 20 | nf_font_t nf_font(const char * font_name, nf_font_params_t params); 21 | 22 | void nf_free(nf_font_t font); 23 | 24 | // returns < 0 on error 25 | int nf_print( 26 | void * bitmap, uint16_t w, uint16_t h, 27 | nf_font_t font, nf_feature_t * features, size_t features_count, 28 | nf_aabb_t * result_rect, const char * text, ...); 29 | ``` 30 | 31 | The only available bitmap format so far is B8G8R8A8_UNORM with premultiplied alpha, because it's the only color format we can get from DirectWrite natively. 32 | 33 | ### Examples 34 | 35 | Rendering on Windows 10: 36 | ![win10 font screenshot](screenshot_win10.png) 37 | 38 | Rendering on OSX retina (screenshot was downscaled to 50%): 39 | ![osx retina font screenshot](screenshot_osx.png) 40 | 41 | ### TODO 42 | 43 | - One header deployment, aka stb. 44 | - Support font params on Core Text. 45 | - Support font features on Core Text. 46 | - More layout/rich text features. 47 | - Check Core Text implementation on memory leaks. 48 | -------------------------------------------------------------------------------- /screenshot_osx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimon/nativefonts/b9054b44ca895af341d0e9134ac74b173613fb45/screenshot_osx.png -------------------------------------------------------------------------------- /screenshot_win10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jimon/nativefonts/b9054b44ca895af341d0e9134ac74b173613fb45/screenshot_win10.png -------------------------------------------------------------------------------- /src/dwrite_c.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This file has no copyright assigned and is placed in the Public Domain. 3 | * This file is part of the mingw-w64 runtime package. 4 | * No warranty is given; refer to the file DISCLAIMER.PD within this package. 5 | */ 6 | /** 7 | * Stripped version. Only definitions needed by libass. Contains fixes to 8 | * make it compile with C. Also needed on MSVC. 9 | */ 10 | #ifndef __INC_DWRITE__ 11 | #define __INC_DWRITE__ 12 | 13 | #define DWRITEAPI DECLSPEC_IMPORT 14 | 15 | #include 16 | 17 | typedef struct IDWriteFactory IDWriteFactory; 18 | typedef struct IDWriteFont IDWriteFont; 19 | typedef struct IDWriteFontCollection IDWriteFontCollection; 20 | typedef struct IDWriteFontFace IDWriteFontFace; 21 | typedef struct IDWriteFontFamily IDWriteFontFamily; 22 | typedef struct IDWriteFontList IDWriteFontList; 23 | typedef struct IDWriteFontFile IDWriteFontFile; 24 | typedef struct IDWriteFontFileLoader IDWriteFontFileLoader; 25 | typedef struct IDWriteFontFileStream IDWriteFontFileStream; 26 | typedef struct IDWriteInlineObject IDWriteInlineObject; 27 | typedef struct IDWriteLocalizedStrings IDWriteLocalizedStrings; 28 | typedef struct IDWritePixelSnapping IDWritePixelSnapping; 29 | typedef struct IDWriteTextFormat IDWriteTextFormat; 30 | typedef struct IDWriteTextLayout IDWriteTextLayout; 31 | typedef struct IDWriteTextRenderer IDWriteTextRenderer; 32 | 33 | #include 34 | 35 | typedef enum DWRITE_INFORMATIONAL_STRING_ID { 36 | DWRITE_INFORMATIONAL_STRING_NONE = 0, 37 | DWRITE_INFORMATIONAL_STRING_COPYRIGHT_NOTICE, 38 | DWRITE_INFORMATIONAL_STRING_VERSION_STRINGS, 39 | DWRITE_INFORMATIONAL_STRING_TRADEMARK, 40 | DWRITE_INFORMATIONAL_STRING_MANUFACTURER, 41 | DWRITE_INFORMATIONAL_STRING_DESIGNER, 42 | DWRITE_INFORMATIONAL_STRING_DESIGNER_URL, 43 | DWRITE_INFORMATIONAL_STRING_DESCRIPTION, 44 | DWRITE_INFORMATIONAL_STRING_FONT_VENDOR_URL, 45 | DWRITE_INFORMATIONAL_STRING_LICENSE_DESCRIPTION, 46 | DWRITE_INFORMATIONAL_STRING_LICENSE_INFO_URL, 47 | DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, 48 | DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES, 49 | DWRITE_INFORMATIONAL_STRING_PREFERRED_FAMILY_NAMES, 50 | DWRITE_INFORMATIONAL_STRING_PREFERRED_SUBFAMILY_NAMES, 51 | DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT, 52 | DWRITE_INFORMATIONAL_STRING_FULL_NAME, 53 | DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME, 54 | DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME 55 | } DWRITE_INFORMATIONAL_STRING_ID; 56 | 57 | typedef enum DWRITE_FACTORY_TYPE { 58 | DWRITE_FACTORY_TYPE_SHARED = 0, 59 | DWRITE_FACTORY_TYPE_ISOLATED 60 | } DWRITE_FACTORY_TYPE; 61 | 62 | typedef enum DWRITE_FONT_FACE_TYPE { 63 | DWRITE_FONT_FACE_TYPE_CFF = 0, 64 | DWRITE_FONT_FACE_TYPE_TRUETYPE, 65 | DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION, 66 | DWRITE_FONT_FACE_TYPE_TYPE1, 67 | DWRITE_FONT_FACE_TYPE_VECTOR, 68 | DWRITE_FONT_FACE_TYPE_BITMAP, 69 | DWRITE_FONT_FACE_TYPE_UNKNOWN, 70 | DWRITE_FONT_FACE_TYPE_RAW_CFF 71 | } DWRITE_FONT_FACE_TYPE; 72 | 73 | typedef enum DWRITE_FONT_SIMULATIONS { 74 | DWRITE_FONT_SIMULATIONS_NONE = 0x0000, 75 | DWRITE_FONT_SIMULATIONS_BOLD = 0x0001, 76 | DWRITE_FONT_SIMULATIONS_OBLIQUE = 0x0002 77 | } DWRITE_FONT_SIMULATIONS; 78 | 79 | typedef enum DWRITE_FONT_STRETCH { 80 | DWRITE_FONT_STRETCH_UNDEFINED = 0, 81 | DWRITE_FONT_STRETCH_ULTRA_CONDENSED = 1, 82 | DWRITE_FONT_STRETCH_EXTRA_CONDENSED = 2, 83 | DWRITE_FONT_STRETCH_CONDENSED = 3, 84 | DWRITE_FONT_STRETCH_SEMI_CONDENSED = 4, 85 | DWRITE_FONT_STRETCH_NORMAL = 5, 86 | DWRITE_FONT_STRETCH_MEDIUM = 5, 87 | DWRITE_FONT_STRETCH_SEMI_EXPANDED = 6, 88 | DWRITE_FONT_STRETCH_EXPANDED = 7, 89 | DWRITE_FONT_STRETCH_EXTRA_EXPANDED = 8, 90 | DWRITE_FONT_STRETCH_ULTRA_EXPANDED = 9 91 | } DWRITE_FONT_STRETCH; 92 | 93 | typedef enum DWRITE_FONT_STYLE { 94 | DWRITE_FONT_STYLE_NORMAL = 0, 95 | DWRITE_FONT_STYLE_OBLIQUE, 96 | DWRITE_FONT_STYLE_ITALIC 97 | } DWRITE_FONT_STYLE; 98 | 99 | typedef enum DWRITE_FONT_WEIGHT { 100 | DWRITE_FONT_WEIGHT_MEDIUM = 500, 101 | /* rest dropped */ 102 | } DWRITE_FONT_WEIGHT; 103 | 104 | typedef struct DWRITE_FONT_METRICS { 105 | UINT16 designUnitsPerEm; 106 | UINT16 ascent; 107 | UINT16 descent; 108 | INT16 lineGap; 109 | UINT16 capHeight; 110 | UINT16 xHeight; 111 | INT16 underlinePosition; 112 | UINT16 underlineThickness; 113 | INT16 strikethroughPosition; 114 | UINT16 strikethroughThickness; 115 | } DWRITE_FONT_METRICS; 116 | 117 | typedef struct DWRITE_GLYPH_OFFSET DWRITE_GLYPH_OFFSET; 118 | 119 | typedef struct DWRITE_GLYPH_RUN { 120 | IDWriteFontFace *fontFace; 121 | FLOAT fontEmSize; 122 | UINT32 glyphCount; 123 | const UINT16 *glyphIndices; 124 | const FLOAT *glyphAdvances; 125 | const DWRITE_GLYPH_OFFSET *glyphOffsets; 126 | BOOL isSideways; 127 | UINT32 bidiLevel; 128 | } DWRITE_GLYPH_RUN; 129 | 130 | typedef struct DWRITE_GLYPH_RUN_DESCRIPTION DWRITE_GLYPH_RUN_DESCRIPTION; 131 | typedef struct DWRITE_HIT_TEST_METRICS DWRITE_HIT_TEST_METRICS; 132 | typedef struct DWRITE_LINE_METRICS DWRITE_LINE_METRICS; 133 | typedef struct DWRITE_MATRIX DWRITE_MATRIX; 134 | typedef struct DWRITE_STRIKETHROUGH DWRITE_STRIKETHROUGH; 135 | typedef struct DWRITE_TEXT_METRICS DWRITE_TEXT_METRICS; 136 | 137 | typedef struct DWRITE_TEXT_RANGE { 138 | UINT32 startPosition; 139 | UINT32 length; 140 | } DWRITE_TEXT_RANGE; 141 | 142 | typedef struct DWRITE_TRIMMING DWRITE_TRIMMING; 143 | typedef struct DWRITE_UNDERLINE DWRITE_UNDERLINE; 144 | 145 | #ifndef __MINGW_DEF_ARG_VAL 146 | #ifdef __cplusplus 147 | #define __MINGW_DEF_ARG_VAL(x) = x 148 | #else 149 | #define __MINGW_DEF_ARG_VAL(x) 150 | #endif 151 | #endif 152 | 153 | #undef INTERFACE 154 | #define INTERFACE IDWriteFactory 155 | DECLARE_INTERFACE_(IDWriteFactory,IUnknown) 156 | { 157 | BEGIN_INTERFACE 158 | 159 | #ifndef __cplusplus 160 | /* IUnknown methods */ 161 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE; 162 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 163 | STDMETHOD_(ULONG, Release)(THIS) PURE; 164 | #endif 165 | 166 | /* IDWriteFactory methods */ 167 | STDMETHOD(GetSystemFontCollection)(THIS_ 168 | IDWriteFontCollection **fontCollection, 169 | BOOL checkForUpdates __MINGW_DEF_ARG_VAL(FALSE)) PURE; 170 | 171 | STDMETHOD(dummy1)(THIS); 172 | STDMETHOD(dummy2)(THIS); 173 | STDMETHOD(dummy3)(THIS); 174 | STDMETHOD(dummy4)(THIS); 175 | STDMETHOD(dummy5)(THIS); 176 | STDMETHOD(dummy6)(THIS); 177 | STDMETHOD(dummy7)(THIS); 178 | STDMETHOD(dummy8)(THIS); 179 | STDMETHOD(dummy9)(THIS); 180 | STDMETHOD(dummy10)(THIS); 181 | STDMETHOD(dummy11)(THIS); 182 | 183 | STDMETHOD(CreateTextFormat)(THIS_ 184 | WCHAR const *fontFamilyName, 185 | IDWriteFontCollection *fontCollection, 186 | DWRITE_FONT_WEIGHT fontWeight, 187 | DWRITE_FONT_STYLE fontStyle, 188 | DWRITE_FONT_STRETCH fontStretch, 189 | FLOAT fontSize, 190 | WCHAR const *localeName, 191 | IDWriteTextFormat **textFormat) PURE; 192 | 193 | STDMETHOD(dummy12)(THIS); 194 | STDMETHOD(dummy13)(THIS); 195 | 196 | STDMETHOD(CreateTextLayout)(THIS_ 197 | WCHAR const *string, 198 | UINT32 stringLength, 199 | IDWriteTextFormat *textFormat, 200 | FLOAT maxWidth, 201 | FLOAT maxHeight, 202 | IDWriteTextLayout **textLayout) PURE; 203 | 204 | /* remainder dropped */ 205 | END_INTERFACE 206 | }; 207 | #ifdef COBJMACROS 208 | #define IDWriteFactory_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) 209 | #define IDWriteFactory_AddRef(This) (This)->lpVtbl->AddRef(This) 210 | #define IDWriteFactory_Release(This) (This)->lpVtbl->Release(This) 211 | #define IDWriteFactory_GetSystemFontCollection(This,fontCollection,checkForUpdates) (This)->lpVtbl->GetSystemFontCollection(This,fontCollection,checkForUpdates) 212 | #define IDWriteFactory_CreateTextFormat(This,fontFamilyName,fontCollection,fontWeight,fontStyle,fontStretch,fontSize,localeName,textFormat) (This)->lpVtbl->CreateTextFormat(This,fontFamilyName,fontCollection,fontWeight,fontStyle,fontStretch,fontSize,localeName,textFormat) 213 | #define IDWriteFactory_CreateTextLayout(This,string,stringLength,textFormat,maxWidth,maxHeight,textLayout) (This)->lpVtbl->CreateTextLayout(This,string,stringLength,textFormat,maxWidth,maxHeight,textLayout) 214 | #endif /*COBJMACROS*/ 215 | 216 | #undef INTERFACE 217 | #define INTERFACE IDWriteFont 218 | DECLARE_INTERFACE_(IDWriteFont,IUnknown) 219 | { 220 | BEGIN_INTERFACE 221 | 222 | #ifndef __cplusplus 223 | /* IUnknown methods */ 224 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE; 225 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 226 | STDMETHOD_(ULONG, Release)(THIS) PURE; 227 | #endif 228 | 229 | /* IDWriteFont methods */ 230 | STDMETHOD(GetFontFamily)(THIS_ 231 | IDWriteFontFamily **fontFamily) PURE; 232 | 233 | STDMETHOD_(DWRITE_FONT_WEIGHT, GetWeight)(THIS) PURE; 234 | STDMETHOD_(DWRITE_FONT_STRETCH, GetStretch)(THIS) PURE; 235 | STDMETHOD_(DWRITE_FONT_STYLE, GetStyle)(THIS) PURE; 236 | STDMETHOD_(BOOL, IsSymbolFont)(THIS) PURE; 237 | 238 | STDMETHOD(GetFaceNames)(THIS_ 239 | IDWriteLocalizedStrings **names) PURE; 240 | 241 | STDMETHOD(GetInformationalStrings)(THIS_ 242 | DWRITE_INFORMATIONAL_STRING_ID informationalStringID, 243 | IDWriteLocalizedStrings **informationalStrings, 244 | BOOL *exists) PURE; 245 | 246 | STDMETHOD_(DWRITE_FONT_SIMULATIONS, GetSimulations)(THIS) PURE; 247 | 248 | STDMETHOD_(void, GetMetrics)(THIS_ 249 | DWRITE_FONT_METRICS *fontMetrics) PURE; 250 | 251 | STDMETHOD(HasCharacter)(THIS_ 252 | UINT32 unicodeValue, 253 | BOOL *exists) PURE; 254 | 255 | STDMETHOD(CreateFontFace)(THIS_ 256 | IDWriteFontFace **fontFace) PURE; 257 | 258 | END_INTERFACE 259 | }; 260 | #ifdef COBJMACROS 261 | #define IDWriteFont_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) 262 | #define IDWriteFont_AddRef(This) (This)->lpVtbl->AddRef(This) 263 | #define IDWriteFont_Release(This) (This)->lpVtbl->Release(This) 264 | #define IDWriteFont_CreateFontFace(This,fontFace) (This)->lpVtbl->CreateFontFace(This,fontFace) 265 | #define IDWriteFont_GetFaceNames(This,names) (This)->lpVtbl->GetFaceNames(This,names) 266 | #define IDWriteFont_GetFontFamily(This,fontFamily) (This)->lpVtbl->GetFontFamily(This,fontFamily) 267 | #define IDWriteFont_GetInformationalStrings(This,informationalStringID,informationalStrings,exists) (This)->lpVtbl->GetInformationalStrings(This,informationalStringID,informationalStrings,exists) 268 | #define IDWriteFont_GetMetrics(This,fontMetrics) (This)->lpVtbl->GetMetrics(This,fontMetrics) 269 | #define IDWriteFont_GetSimulations(This) (This)->lpVtbl->GetSimulations(This) 270 | #define IDWriteFont_GetStretch(This) (This)->lpVtbl->GetStretch(This) 271 | #define IDWriteFont_GetStyle(This) (This)->lpVtbl->GetStyle(This) 272 | #define IDWriteFont_GetWeight(This) (This)->lpVtbl->GetWeight(This) 273 | #define IDWriteFont_HasCharacter(This,unicodeValue,exists) (This)->lpVtbl->HasCharacter(This,unicodeValue,exists) 274 | #define IDWriteFont_IsSymbolFont(This) (This)->lpVtbl->IsSymbolFont(This) 275 | #endif /*COBJMACROS*/ 276 | 277 | #undef INTERFACE 278 | #define INTERFACE IDWriteFontCollection 279 | DECLARE_INTERFACE_(IDWriteFontCollection,IUnknown) 280 | { 281 | BEGIN_INTERFACE 282 | 283 | #ifndef __cplusplus 284 | /* IUnknown methods */ 285 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE; 286 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 287 | STDMETHOD_(ULONG, Release)(THIS) PURE; 288 | #endif 289 | 290 | /* IDWriteFontCollection methods */ 291 | STDMETHOD_(UINT32, GetFontFamilyCount)(THIS) PURE; 292 | 293 | STDMETHOD(GetFontFamily)(THIS_ 294 | UINT32 index, 295 | IDWriteFontFamily **fontFamily) PURE; 296 | 297 | STDMETHOD(FindFamilyName)(THIS_ 298 | WCHAR const *familyName, 299 | UINT32 *index, 300 | BOOL *exists) PURE; 301 | 302 | STDMETHOD(GetFontFromFontFace)(THIS_ 303 | IDWriteFontFace* fontFace, 304 | IDWriteFont **font) PURE; 305 | 306 | END_INTERFACE 307 | }; 308 | #ifdef COBJMACROS 309 | #define IDWriteFontCollection_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) 310 | #define IDWriteFontCollection_AddRef(This) (This)->lpVtbl->AddRef(This) 311 | #define IDWriteFontCollection_Release(This) (This)->lpVtbl->Release(This) 312 | #define IDWriteFontCollection_FindFamilyName(This,familyName,index,exists) (This)->lpVtbl->FindFamilyName(This,familyName,index,exists) 313 | #define IDWriteFontCollection_GetFontFamily(This,index,fontFamily) (This)->lpVtbl->GetFontFamily(This,index,fontFamily) 314 | #define IDWriteFontCollection_GetFontFamilyCount(This) (This)->lpVtbl->GetFontFamilyCount(This) 315 | #define IDWriteFontCollection_GetFontFromFontFace(This,fontFace,font) (This)->lpVtbl->GetFontFromFontFace(This,fontFace,font) 316 | #endif /*COBJMACROS*/ 317 | 318 | #undef INTERFACE 319 | #define INTERFACE IDWriteFontFace 320 | DECLARE_INTERFACE_(IDWriteFontFace,IUnknown) 321 | { 322 | BEGIN_INTERFACE 323 | 324 | #ifndef __cplusplus 325 | /* IUnknown methods */ 326 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE; 327 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 328 | STDMETHOD_(ULONG, Release)(THIS) PURE; 329 | #endif 330 | 331 | /* IDWriteFontFace methods */ 332 | STDMETHOD_(DWRITE_FONT_FACE_TYPE, GetType)(THIS) PURE; 333 | 334 | STDMETHOD(GetFiles)(THIS_ 335 | UINT32 *numberOfFiles, 336 | IDWriteFontFile **fontFiles) PURE; 337 | 338 | /* rest dropped */ 339 | END_INTERFACE 340 | }; 341 | #ifdef COBJMACROS 342 | #define IDWriteFontFace_Release(This) (This)->lpVtbl->Release(This) 343 | #define IDWriteFontFace_GetType(This) (This)->lpVtbl->GetType(This) 344 | #define IDWriteFontFace_GetFiles(This,fontFiles,b) (This)->lpVtbl->GetFiles(This,fontFiles,b) 345 | #endif /*COBJMACROS*/ 346 | 347 | #undef INTERFACE 348 | #define INTERFACE IDWriteFontFamily 349 | DECLARE_INTERFACE_(IDWriteFontFamily,IDWriteFontList) 350 | { 351 | BEGIN_INTERFACE 352 | 353 | #ifndef __cplusplus 354 | /* IUnknown methods */ 355 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE; 356 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 357 | STDMETHOD_(ULONG, Release)(THIS) PURE; 358 | 359 | /* IDWriteFontList methods */ 360 | STDMETHOD(GetFontCollection)(THIS_ 361 | IDWriteFontCollection** fontCollection) PURE; 362 | 363 | STDMETHOD_(UINT32, GetFontCount)(THIS) PURE; 364 | 365 | STDMETHOD(GetFont)(THIS_ 366 | UINT32 index, 367 | IDWriteFont **font) PURE; 368 | #endif 369 | 370 | /* IDWriteFontFamily methods */ 371 | STDMETHOD(GetFamilyNames)(THIS_ 372 | IDWriteLocalizedStrings **names) PURE; 373 | 374 | /* rest dropped */ 375 | END_INTERFACE 376 | }; 377 | #ifdef COBJMACROS 378 | #define IDWriteFontFamily_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) 379 | #define IDWriteFontFamily_AddRef(This) (This)->lpVtbl->AddRef(This) 380 | #define IDWriteFontFamily_Release(This) (This)->lpVtbl->Release(This) 381 | #define IDWriteFontFamily_GetFont(This,index,font) (This)->lpVtbl->GetFont(This,index,font) 382 | #define IDWriteFontFamily_GetFontCount(This) (This)->lpVtbl->GetFontCount(This) 383 | #define IDWriteFontFamily_GetFamilyNames(This,names) (This)->lpVtbl->GetFamilyNames(This,names) 384 | #endif /*COBJMACROS*/ 385 | 386 | #undef INTERFACE 387 | #define INTERFACE IDWriteFontFile 388 | DECLARE_INTERFACE_(IDWriteFontFile,IUnknown) 389 | { 390 | BEGIN_INTERFACE 391 | 392 | #ifndef __cplusplus 393 | /* IUnknown methods */ 394 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE; 395 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 396 | STDMETHOD_(ULONG, Release)(THIS) PURE; 397 | #endif 398 | 399 | /* IDWriteFontFile methods */ 400 | STDMETHOD(GetReferenceKey)(THIS_ 401 | void const **fontFileReferenceKey, 402 | UINT32 *fontFileReferenceKeySize) PURE; 403 | 404 | STDMETHOD(GetLoader)(THIS_ 405 | IDWriteFontFileLoader **fontFileLoader) PURE; 406 | 407 | /* rest dropped */ 408 | END_INTERFACE 409 | }; 410 | #ifdef COBJMACROS 411 | #define IDWriteFontFile_Release(This) (This)->lpVtbl->Release(This) 412 | #define IDWriteFontFile_GetLoader(This,fontFileLoader) (This)->lpVtbl->GetLoader(This,fontFileLoader) 413 | #define IDWriteFontFile_GetReferenceKey(This,fontFileReferenceKey,fontFileReferenceKeySize) (This)->lpVtbl->GetReferenceKey(This,fontFileReferenceKey,fontFileReferenceKeySize) 414 | #endif /*COBJMACROS*/ 415 | 416 | #undef INTERFACE 417 | #define INTERFACE IDWriteFontFileLoader 418 | DECLARE_INTERFACE_(IDWriteFontFileLoader,IUnknown) 419 | { 420 | BEGIN_INTERFACE 421 | 422 | #ifndef __cplusplus 423 | /* IUnknown methods */ 424 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE; 425 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 426 | STDMETHOD_(ULONG, Release)(THIS) PURE; 427 | #endif 428 | 429 | /* IDWriteFontFileLoader methods */ 430 | STDMETHOD(CreateStreamFromKey)(THIS_ 431 | void const *fontFileReferenceKey, 432 | UINT32 fontFileReferenceKeySize, 433 | IDWriteFontFileStream **fontFileStream) PURE; 434 | 435 | END_INTERFACE 436 | }; 437 | #ifdef COBJMACROS 438 | #define IDWriteFontFileLoader_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) 439 | #define IDWriteFontFileLoader_AddRef(This) (This)->lpVtbl->AddRef(This) 440 | #define IDWriteFontFileLoader_Release(This) (This)->lpVtbl->Release(This) 441 | #define IDWriteFontFileLoader_CreateStreamFromKey(This,fontFileReferenceKey,fontFileReferenceKeySize,fontFileStream) (This)->lpVtbl->CreateStreamFromKey(This,fontFileReferenceKey,fontFileReferenceKeySize,fontFileStream) 442 | #endif /*COBJMACROS*/ 443 | 444 | #undef INTERFACE 445 | #define INTERFACE IDWriteFontFileStream 446 | DECLARE_INTERFACE_(IDWriteFontFileStream,IUnknown) 447 | { 448 | BEGIN_INTERFACE 449 | 450 | #ifndef __cplusplus 451 | /* IUnknown methods */ 452 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE; 453 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 454 | STDMETHOD_(ULONG, Release)(THIS) PURE; 455 | #endif 456 | 457 | /* IDWriteFontFileStream methods */ 458 | STDMETHOD(ReadFileFragment)(THIS_ 459 | void const **fragmentStart, 460 | UINT64 fileOffset, 461 | UINT64 fragmentSize, 462 | void** fragmentContext) PURE; 463 | 464 | STDMETHOD_(void, ReleaseFileFragment)(THIS_ 465 | void *fragmentContext) PURE; 466 | 467 | STDMETHOD(GetFileSize)(THIS_ 468 | UINT64 *fileSize) PURE; 469 | 470 | STDMETHOD(GetLastWriteTime)(THIS_ 471 | UINT64 *lastWriteTime) PURE; 472 | 473 | END_INTERFACE 474 | }; 475 | #ifdef COBJMACROS 476 | #define IDWriteFontFileStream_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject) 477 | #define IDWriteFontFileStream_AddRef(This) (This)->lpVtbl->AddRef(This) 478 | #define IDWriteFontFileStream_Release(This) (This)->lpVtbl->Release(This) 479 | #define IDWriteFontFileStream_GetFileSize(This,fileSize) (This)->lpVtbl->GetFileSize(This,fileSize) 480 | #define IDWriteFontFileStream_ReadFileFragment(This,fragmentStart,fileOffset,fragmentSize,fragmentContext) (This)->lpVtbl->ReadFileFragment(This,fragmentStart,fileOffset,fragmentSize,fragmentContext) 481 | #define IDWriteFontFileStream_ReleaseFileFragment(This,fragmentContext) (This)->lpVtbl->ReleaseFileFragment(This,fragmentContext) 482 | #endif /*COBJMACROS*/ 483 | 484 | #undef INTERFACE 485 | #define INTERFACE IDWriteLocalizedStrings 486 | DECLARE_INTERFACE_(IDWriteLocalizedStrings,IUnknown) 487 | { 488 | BEGIN_INTERFACE 489 | 490 | #ifndef __cplusplus 491 | /* IUnknown methods */ 492 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE; 493 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 494 | STDMETHOD_(ULONG, Release)(THIS) PURE; 495 | #endif 496 | 497 | /* IDWriteLocalizedStrings methods */ 498 | STDMETHOD_(UINT32, GetCount)(THIS) PURE; 499 | 500 | STDMETHOD(dummy1)(THIS); 501 | STDMETHOD(dummy2)(THIS); 502 | STDMETHOD(dummy3)(THIS); 503 | STDMETHOD(dummy4)(THIS); 504 | 505 | STDMETHOD(GetString)(THIS_ 506 | UINT32 index, 507 | WCHAR *stringBuffer, 508 | UINT32 size) PURE; 509 | 510 | END_INTERFACE 511 | }; 512 | #ifdef COBJMACROS 513 | #define IDWriteLocalizedStrings_Release(This) (This)->lpVtbl->Release(This) 514 | #define IDWriteLocalizedStrings_GetCount(This) (This)->lpVtbl->GetCount(This) 515 | #define IDWriteLocalizedStrings_GetString(This,index,stringBuffer,size) (This)->lpVtbl->GetString(This,index,stringBuffer,size) 516 | #endif /*COBJMACROS*/ 517 | 518 | #undef INTERFACE 519 | #define INTERFACE IDWriteTextFormat 520 | DECLARE_INTERFACE_(IDWriteTextFormat,IUnknown) 521 | { 522 | BEGIN_INTERFACE 523 | 524 | #ifndef __cplusplus 525 | /* IUnknown methods */ 526 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE; 527 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 528 | STDMETHOD_(ULONG, Release)(THIS) PURE; 529 | #endif 530 | 531 | /* IDWriteTextFormat methods */ 532 | /* rest dropped */ 533 | END_INTERFACE 534 | }; 535 | #ifdef COBJMACROS 536 | #define IDWriteTextFormat_Release(This) (This)->lpVtbl->Release(This) 537 | #endif /*COBJMACROS*/ 538 | 539 | #undef INTERFACE 540 | #define INTERFACE IDWriteTextLayout 541 | DECLARE_INTERFACE_(IDWriteTextLayout,IDWriteTextFormat) 542 | { 543 | BEGIN_INTERFACE 544 | 545 | #ifndef __cplusplus 546 | /* IUnknown methods */ 547 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE; 548 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 549 | STDMETHOD_(ULONG, Release)(THIS) PURE; 550 | 551 | /* IDWriteTextFormat methods */ 552 | STDMETHOD(dummy1)(THIS); 553 | STDMETHOD(dummy2)(THIS); 554 | STDMETHOD(dummy3)(THIS); 555 | STDMETHOD(dummy4)(THIS); 556 | STDMETHOD(dummy5)(THIS); 557 | STDMETHOD(dummy6)(THIS); 558 | STDMETHOD(dummy7)(THIS); 559 | STDMETHOD(dummy8)(THIS); 560 | STDMETHOD(dummy9)(THIS); 561 | STDMETHOD(dummy10)(THIS); 562 | STDMETHOD(dummy11)(THIS); 563 | STDMETHOD(dummy12)(THIS); 564 | STDMETHOD(dummy13)(THIS); 565 | STDMETHOD(dummy14)(THIS); 566 | STDMETHOD(dummy15)(THIS); 567 | STDMETHOD(dummy16)(THIS); 568 | STDMETHOD(dummy17)(THIS); 569 | STDMETHOD(dummy18)(THIS); 570 | STDMETHOD(dummy19)(THIS); 571 | STDMETHOD(dummy20)(THIS); 572 | STDMETHOD(dummy21)(THIS); 573 | STDMETHOD(dummy22)(THIS); 574 | STDMETHOD(dummy23)(THIS); 575 | STDMETHOD(dummy24)(THIS); 576 | STDMETHOD(dummy25)(THIS); 577 | #endif 578 | 579 | /* IDWriteTextLayout methods */ 580 | STDMETHOD(dummy26)(THIS); 581 | STDMETHOD(dummy27)(THIS); 582 | STDMETHOD(dummy28)(THIS); 583 | STDMETHOD(dummy29)(THIS); 584 | STDMETHOD(dummy30)(THIS); 585 | STDMETHOD(dummy31)(THIS); 586 | STDMETHOD(dummy32)(THIS); 587 | STDMETHOD(dummy33)(THIS); 588 | STDMETHOD(dummy34)(THIS); 589 | STDMETHOD(dummy35)(THIS); 590 | STDMETHOD(dummy36)(THIS); 591 | STDMETHOD(dummy37)(THIS); 592 | STDMETHOD(dummy38)(THIS); 593 | STDMETHOD(dummy39)(THIS); 594 | STDMETHOD(dummy40)(THIS); 595 | STDMETHOD(dummy41)(THIS); 596 | STDMETHOD(dummy42)(THIS); 597 | STDMETHOD(dummy43)(THIS); 598 | STDMETHOD(dummy44)(THIS); 599 | STDMETHOD(dummy45)(THIS); 600 | STDMETHOD(dummy46)(THIS); 601 | STDMETHOD(dummy47)(THIS); 602 | STDMETHOD(dummy48)(THIS); 603 | STDMETHOD(dummy49)(THIS); 604 | STDMETHOD(dummy50)(THIS); 605 | STDMETHOD(dummy51)(THIS); 606 | STDMETHOD(dummy52)(THIS); 607 | STDMETHOD(dummy53)(THIS); 608 | STDMETHOD(dummy54)(THIS); 609 | STDMETHOD(dummy55)(THIS); 610 | STDMETHOD(Draw)(THIS_ 611 | void *clientDrawingContext, 612 | IDWriteTextRenderer *renderer, 613 | FLOAT originX, 614 | FLOAT originY) PURE; 615 | /* rest dropped */ 616 | END_INTERFACE 617 | }; 618 | #ifdef COBJMACROS 619 | #define IDWriteTextLayout_Release(This) (This)->lpVtbl->Release(This) 620 | #define IDWriteTextLayout_Draw(This,clientDrawingContext,renderer,originX,originY) (This)->lpVtbl->Draw(This,clientDrawingContext,renderer,originX,originY) 621 | #endif /*COBJMACROS*/ 622 | 623 | #undef INTERFACE 624 | #define INTERFACE IDWriteTextRenderer 625 | DECLARE_INTERFACE_(IDWriteTextRenderer,IDWritePixelSnapping) 626 | { 627 | BEGIN_INTERFACE 628 | 629 | #ifndef __cplusplus 630 | /* IUnknown methods */ 631 | STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **ppvObject) PURE; 632 | STDMETHOD_(ULONG, AddRef)(THIS) PURE; 633 | STDMETHOD_(ULONG, Release)(THIS) PURE; 634 | 635 | /* IDWritePixelSnapping methods */ 636 | STDMETHOD(IsPixelSnappingDisabled)(THIS_ 637 | void *clientDrawingContext, 638 | BOOL *isDisabled) PURE; 639 | STDMETHOD(GetCurrentTransform)(THIS_ 640 | void *clientDrawingContext, 641 | DWRITE_MATRIX *transform) PURE; 642 | STDMETHOD(GetPixelsPerDip)(THIS_ 643 | void *clientDrawingContext, 644 | FLOAT *pixelsPerDip) PURE; 645 | #endif 646 | 647 | /* IDWriteTextRenderer methods */ 648 | STDMETHOD(DrawGlyphRun)(THIS_ 649 | void *clientDrawingContext, 650 | FLOAT baselineOriginX, 651 | FLOAT baselineOriginY, 652 | DWRITE_MEASURING_MODE measuringMode, 653 | DWRITE_GLYPH_RUN const *glyphRun, 654 | DWRITE_GLYPH_RUN_DESCRIPTION const *glyphRunDescription, 655 | IUnknown* clientDrawingEffect) PURE; 656 | STDMETHOD(DrawUnderline)(THIS_ 657 | void *clientDrawingContext, 658 | FLOAT baselineOriginX, 659 | FLOAT baselineOriginY, 660 | DWRITE_UNDERLINE const *underline, 661 | IUnknown *clientDrawingEffect) PURE; 662 | STDMETHOD(DrawStrikethrough)(THIS_ 663 | void *clientDrawingContext, 664 | FLOAT baselineOriginX, 665 | FLOAT baselineOriginY, 666 | DWRITE_STRIKETHROUGH const *strikethrough, 667 | IUnknown* clientDrawingEffect) PURE; 668 | STDMETHOD(DrawInlineObject)(THIS_ 669 | void *clientDrawingContext, 670 | FLOAT originX, 671 | FLOAT originY, 672 | IDWriteInlineObject *inlineObject, 673 | BOOL isSideways, 674 | BOOL isRightToLeft, 675 | IUnknown *clientDrawingEffect) PURE; 676 | 677 | END_INTERFACE 678 | }; 679 | 680 | DEFINE_GUID(IID_IDWriteFactory, 0xb859ee5a,0xd838,0x4b5b,0xa2,0xe8,0x1a,0xdc,0x7d,0x93,0xdb,0x48); 681 | DEFINE_GUID(IID_IDWritePixelSnapping, 0xeaf3a2da,0xecf4,0x4d24,0xb6,0x44,0xb3,0x4f,0x68,0x42,0x02,0x4b); 682 | DEFINE_GUID(IID_IDWriteTextRenderer, 0xef8a8135,0x5cc6,0x45fe,0x88,0x25,0xc5,0xa0,0x72,0x4e,0xb8,0x19); 683 | 684 | // ---- additional functions 685 | HRESULT WINAPI DWriteCreateFactory( 686 | _In_ DWRITE_FACTORY_TYPE factoryType, 687 | _In_ REFIID iid, 688 | _COM_Outptr_ IUnknown **factory 689 | ); 690 | 691 | #endif /* __INC_DWRITE__ */ 692 | -------------------------------------------------------------------------------- /src/nativefonts_coretext.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #ifdef NF_PLATFORM_CORETEXT 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | nf_system_info_t nf_system_info() 12 | { 13 | nf_system_info_t ret; 14 | ret.bitmap = NF_BITMAP_B8G8R8A8_UNORM_PMA; 15 | ret.max_width = NF_MAX_WIDTH; 16 | ret.max_height = NF_MAX_HEIGHT; 17 | ret.ppi_x = 0.0f; // TODO 18 | ret.ppi_y = 0.0f; 19 | return ret; 20 | } 21 | 22 | nf_font_t nf_font(const char * font_name, nf_font_params_t params) 23 | { 24 | // TODO traits ? 25 | 26 | if(font_name) 27 | { 28 | CFStringRef font_name_ref = CFStringCreateWithCString( 29 | NULL, 30 | font_name, 31 | kCFStringEncodingUTF8 32 | ); 33 | 34 | CTFontRef font = CTFontCreateWithName( 35 | font_name_ref, 36 | params.size_in_pt, 37 | nil 38 | ); 39 | 40 | return (nf_font_t)font; 41 | } 42 | else 43 | { 44 | CTFontRef font = CTFontCreateUIFontForLanguage( 45 | kCTFontUIFontUser, 46 | params.size_in_pt, 47 | nil 48 | ); 49 | 50 | return (nf_font_t)font; 51 | } 52 | } 53 | 54 | void nf_free(nf_font_t font) 55 | { 56 | if(font) 57 | CFRelease((CTFontRef)font); 58 | } 59 | 60 | // based on stackoverflow 61 | static CGSize nf_frame_size(CTFrameRef frame) 62 | { 63 | CFArrayRef lines = CTFrameGetLines(frame); 64 | CFIndex numLines = CFArrayGetCount(lines); 65 | 66 | if(numLines <= 0) 67 | return CGSizeMake(0.0f, 0.0f); 68 | 69 | CGFloat maxWidth = 0.0f; 70 | 71 | for(CFIndex index = 0; index < numLines; ++index) 72 | { 73 | CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(lines, index); 74 | CGFloat width = CTLineGetTypographicBounds(line, NULL, NULL, NULL); 75 | 76 | if(width > maxWidth) 77 | maxWidth = width; 78 | } 79 | 80 | CGFloat ascent = 0.0f, descent = 0.0f, leading = 0.0f; 81 | CTLineGetTypographicBounds( 82 | (CTLineRef)CFArrayGetValueAtIndex(lines, numLines - 1), 83 | &ascent, 84 | &descent, 85 | &leading 86 | ); 87 | CGFloat lastLineHeight = ascent + descent + leading; 88 | 89 | CGPoint firstLineOrigin; 90 | CTFrameGetLineOrigins(frame, CFRangeMake(0, 1), &firstLineOrigin); 91 | 92 | CGPoint lastLineOrigin; 93 | CTFrameGetLineOrigins(frame, CFRangeMake(numLines - 1, 1), &lastLineOrigin); 94 | 95 | // TODO verify this for all directions 96 | CGFloat textHeight = fabs(firstLineOrigin.y - lastLineOrigin.y) + 97 | lastLineHeight; 98 | 99 | return CGSizeMake(maxWidth, textHeight); 100 | } 101 | 102 | int nf_print( 103 | void * bitmap, uint16_t w, uint16_t h, 104 | nf_font_t font, nf_feature_t * features, size_t features_count, 105 | nf_aabb_t * result_rect, const char * text) 106 | { 107 | CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); 108 | CGFloat components[] = {1.0, 1.0, 1.0, 1.0}; 109 | CGColorRef white = CGColorCreate(rgbColorSpace, components); 110 | CGColorSpaceRelease(rgbColorSpace); 111 | 112 | CFStringRef string_ref = CFStringCreateWithCString( 113 | NULL, 114 | text, 115 | kCFStringEncodingUTF8 116 | ); 117 | 118 | if(!string_ref) 119 | return 0; 120 | 121 | const void * keys[2] = 122 | { 123 | kCTFontAttributeName, 124 | kCTForegroundColorAttributeName 125 | // TODO support features 126 | }; 127 | const void * values[2] = 128 | { 129 | (const void*)font, 130 | white 131 | }; 132 | CFDictionaryRef dict_ref = CFDictionaryCreate( 133 | NULL, 134 | keys, 135 | values, 136 | 2, 137 | &kCFCopyStringDictionaryKeyCallBacks, 138 | &kCFTypeDictionaryValueCallBacks 139 | ); 140 | CFAttributedStringRef text_ref = CFAttributedStringCreate( 141 | NULL, 142 | string_ref, 143 | dict_ref 144 | ); 145 | CFRelease(string_ref); 146 | CFRelease(dict_ref); 147 | CGColorRelease(white); 148 | 149 | CGMutablePathRef path = CGPathCreateMutable(); 150 | CGRect bounds = CGRectMake(0.0, 0.0, w, h); 151 | CGPathAddRect(path, NULL, bounds); 152 | 153 | CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString( 154 | text_ref 155 | ); 156 | CFRelease(text_ref); 157 | CTFrameRef frame = CTFramesetterCreateFrame( 158 | framesetter, 159 | CFRangeMake(0, 0), 160 | path, 161 | NULL 162 | ); 163 | CFRelease(framesetter); 164 | CFRelease(path); 165 | 166 | CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); 167 | CGContextRef ctx = CGBitmapContextCreate( 168 | bitmap, 169 | w, 170 | h, 171 | 8, 172 | w * 4, 173 | space, 174 | kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little); 175 | CGColorSpaceRelease(space); 176 | //CGContextSetRGBFillColor(ctx, 0.0, 0.0, 0.0, 1.0); 177 | //CGContextFillRect(ctx, CGRectMake(0.0, 0.0, w, h)); 178 | CGContextClearRect(ctx, CGRectMake(0.0, 0.0, w, h)); 179 | 180 | CGContextSetTextPosition(ctx, 0.0, 0.0); 181 | CTFrameDraw(frame, ctx); 182 | 183 | CFRelease(ctx); 184 | 185 | if(result_rect) 186 | { 187 | CGSize size = nf_frame_size(frame); 188 | result_rect->x = 0; 189 | result_rect->y = 0; 190 | result_rect->w = size.width; 191 | result_rect->h = size.height; 192 | } 193 | 194 | CFRelease(frame); 195 | return 0; 196 | } 197 | 198 | #endif 199 | 200 | -------------------------------------------------------------------------------- /src/nativefonts_dwrite.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #ifdef NF_PLATFORM_DWRITE 5 | 6 | #ifndef NF_SUPPORT_EXPLAIN_HR 7 | #define NF_SUPPORT_EXPLAIN_HR 1 8 | #endif 9 | 10 | //#define COBJMACROS 11 | //#include 12 | //#include 13 | //#include 14 | //#include "dwrite_c.h" 15 | #include 16 | #include 17 | #include 18 | #include 19 | #if NF_SUPPORT_EXPLAIN_HR 20 | #include 21 | #endif 22 | #include 23 | 24 | // todo don't define here? 25 | #pragma comment(lib, "dwrite.lib") 26 | #pragma comment(lib, "d2d1.lib") 27 | #pragma comment(lib, "D3D10_1.lib") 28 | 29 | typedef struct 30 | { 31 | ID3D10Device1 * d3d_device; 32 | IDWriteFactory * dw_factory; 33 | ID2D1Factory1 * d2d_factory; 34 | 35 | ID3D10Texture2D * d3d_texture1; 36 | ID3D10Texture2D * d3d_texture2; 37 | 38 | ID2D1RenderTarget * d2d_rt; 39 | ID2D1SolidColorBrush * d2d_brush; 40 | 41 | size_t reference_counter; 42 | } nf_context_t; 43 | 44 | nf_context_t ctx = {0}; 45 | 46 | void nf_explain_hr(HRESULT hr, const char * msg) 47 | { 48 | #if NF_SUPPORT_EXPLAIN_HR 49 | _com_error err(hr); 50 | NF_ERROR("%s %s\n", msg, err.ErrorMessage()); 51 | #else 52 | NF_ON_ERROR("%s\n", msg); 53 | #endif 54 | } 55 | 56 | void nf_ctx_free(); 57 | 58 | int nf_ctx_init() 59 | { 60 | if(ctx.reference_counter++) 61 | return 0; 62 | 63 | HRESULT hr = 0; 64 | 65 | if(FAILED(hr = D3D10CreateDevice1( 66 | NULL, D3D10_DRIVER_TYPE_HARDWARE, 67 | NULL, D3D10_CREATE_DEVICE_BGRA_SUPPORT, 68 | D3D10_FEATURE_LEVEL_9_1, D3D10_1_SDK_VERSION, 69 | &ctx.d3d_device))) 70 | { 71 | nf_explain_hr(hr, "can't create d3d10 device"); 72 | nf_ctx_free(); 73 | return -1; 74 | } 75 | 76 | D3D10_TEXTURE2D_DESC texdesc1; 77 | texdesc1.ArraySize = 1; 78 | texdesc1.BindFlags = D3D10_BIND_RENDER_TARGET; 79 | texdesc1.CPUAccessFlags = 0; 80 | texdesc1.Format = DXGI_FORMAT_B8G8R8A8_UNORM; 81 | texdesc1.Width = NF_MAX_WIDTH; 82 | texdesc1.Height = NF_MAX_HEIGHT; 83 | texdesc1.MipLevels = 1; 84 | texdesc1.MiscFlags = 0; 85 | texdesc1.SampleDesc.Count = 1; 86 | texdesc1.SampleDesc.Quality = 0; 87 | texdesc1.Usage = D3D10_USAGE_DEFAULT; 88 | 89 | if(FAILED(hr = ctx.d3d_device->CreateTexture2D( 90 | &texdesc1, 91 | NULL, 92 | &ctx.d3d_texture1))) 93 | { 94 | nf_explain_hr(hr, "can't create d3d10 texture #1"); 95 | nf_ctx_free(); 96 | return -1; 97 | } 98 | 99 | IDXGISurface * dxgi_surface = NULL; 100 | if(FAILED(hr = ctx.d3d_texture1->QueryInterface(&dxgi_surface))) 101 | { 102 | nf_explain_hr(hr, "can't get dxgi interface"); 103 | nf_ctx_free(); 104 | return -1; 105 | } 106 | 107 | D3D10_TEXTURE2D_DESC texdesc2; 108 | texdesc2.ArraySize = 1; 109 | texdesc2.BindFlags = 0; 110 | texdesc2.CPUAccessFlags = D3D10_CPU_ACCESS_READ | D3D10_CPU_ACCESS_WRITE; 111 | texdesc2.Format = DXGI_FORMAT_B8G8R8A8_UNORM; 112 | texdesc2.Width = NF_MAX_WIDTH; 113 | texdesc2.Height = NF_MAX_HEIGHT; 114 | texdesc2.MipLevels = 1; 115 | texdesc2.MiscFlags = 0; 116 | texdesc2.SampleDesc.Count = 1; 117 | texdesc2.SampleDesc.Quality = 0; 118 | texdesc2.Usage = D3D10_USAGE_STAGING; 119 | 120 | if(FAILED(hr = ctx.d3d_device->CreateTexture2D( 121 | &texdesc2, 122 | NULL, 123 | &ctx.d3d_texture2))) 124 | { 125 | nf_explain_hr(hr, "can't create d3d10 texture #2"); 126 | nf_ctx_free(); 127 | return -1; 128 | } 129 | 130 | if(FAILED(hr = DWriteCreateFactory( 131 | DWRITE_FACTORY_TYPE_SHARED, 132 | //&IID_IDWriteFactory, 133 | __uuidof(IDWriteFactory), 134 | (IUnknown**)(&ctx.dw_factory)))) 135 | { 136 | nf_explain_hr(hr, "can't create dwrite factory"); 137 | nf_ctx_free(); 138 | return -1; 139 | } 140 | 141 | D2D1_FACTORY_OPTIONS d2d1_options; 142 | d2d1_options.debugLevel = D2D1_DEBUG_LEVEL_WARNING; 143 | 144 | if(FAILED(hr = D2D1CreateFactory( 145 | D2D1_FACTORY_TYPE_SINGLE_THREADED, 146 | __uuidof(ID2D1Factory1), 147 | &d2d1_options, 148 | (void**)&ctx.d2d_factory))) 149 | { 150 | nf_explain_hr(hr, "can't create d2d1 factory"); 151 | nf_ctx_free(); 152 | return -1; 153 | } 154 | 155 | float ppi_x = 0.0f, ppi_y = 0.0f; 156 | ctx.d2d_factory->GetDesktopDpi(&ppi_x, &ppi_y); 157 | 158 | D2D1_RENDER_TARGET_PROPERTIES d2d1_props; 159 | d2d1_props.dpiX = ppi_x; 160 | d2d1_props.dpiY = ppi_y; 161 | d2d1_props.minLevel = D2D1_FEATURE_LEVEL_DEFAULT; 162 | d2d1_props.pixelFormat.format = DXGI_FORMAT_UNKNOWN; 163 | d2d1_props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; 164 | d2d1_props.type = D2D1_RENDER_TARGET_TYPE_HARDWARE; 165 | d2d1_props.usage = D2D1_RENDER_TARGET_USAGE_NONE; 166 | 167 | if(FAILED(hr = ctx.d2d_factory->CreateDxgiSurfaceRenderTarget( 168 | dxgi_surface, 169 | d2d1_props, 170 | &ctx.d2d_rt))) 171 | { 172 | nf_explain_hr(hr, "can't create d2d1 render target"); 173 | nf_ctx_free(); 174 | return -1; 175 | } 176 | 177 | if(FAILED(hr = ctx.d2d_rt->CreateSolidColorBrush( 178 | D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), 179 | &ctx.d2d_brush))) 180 | { 181 | nf_explain_hr(hr, "can't create d2d1 solid brush"); 182 | nf_ctx_free(); 183 | return -1; 184 | } 185 | 186 | return 0; 187 | 188 | } 189 | 190 | void nf_ctx_free() 191 | { 192 | if(!ctx.reference_counter) 193 | return; 194 | 195 | if(--ctx.reference_counter) 196 | return; 197 | 198 | if(ctx.d2d_brush) 199 | { 200 | ctx.d2d_brush->Release(); 201 | ctx.d2d_brush = NULL; 202 | } 203 | 204 | if(ctx.d2d_rt) 205 | { 206 | ctx.d2d_rt->Release(); 207 | ctx.d2d_rt = NULL; 208 | } 209 | 210 | if(ctx.d3d_texture2) 211 | { 212 | ctx.d3d_texture2->Release(); 213 | ctx.d3d_texture2 = NULL; 214 | } 215 | 216 | if(ctx.d3d_texture1) 217 | { 218 | ctx.d3d_texture1->Release(); 219 | ctx.d3d_texture1 = NULL; 220 | } 221 | 222 | if(ctx.d2d_factory) 223 | { 224 | ctx.d2d_factory->Release(); 225 | ctx.d2d_factory = NULL; 226 | } 227 | if(ctx.dw_factory) 228 | { 229 | ctx.dw_factory->Release(); 230 | ctx.dw_factory = NULL; 231 | } 232 | 233 | if(ctx.d3d_device) 234 | { 235 | ctx.d3d_device->Release(); 236 | ctx.d3d_device = NULL; 237 | } 238 | } 239 | 240 | nf_system_info_t nf_system_info() 241 | { 242 | float ppi_x = 0.0f, ppi_y = 0.0f; 243 | 244 | // instead of doing full initialization, let's just create a d2d1 factory 245 | if(!ctx.reference_counter) 246 | { 247 | HRESULT hr = 0; 248 | if(FAILED(hr = D2D1CreateFactory( 249 | D2D1_FACTORY_TYPE_SINGLE_THREADED, 250 | __uuidof(ID2D1Factory1), 251 | NULL, 252 | (void**)&ctx.d2d_factory))) 253 | { 254 | NF_ERROR("can't create d2d1 factory\n"); 255 | ctx.d2d_factory = NULL; 256 | } 257 | } 258 | ctx.d2d_factory->GetDesktopDpi(&ppi_x, &ppi_y); 259 | if(!ctx.reference_counter && ctx.d2d_factory) 260 | { 261 | ctx.d2d_factory->Release(); 262 | ctx.d2d_factory = NULL; 263 | } 264 | 265 | nf_system_info_t ret; 266 | ret.bitmap = NF_BITMAP_B8G8R8A8_UNORM_PMA; 267 | ret.max_width = NF_MAX_WIDTH; 268 | ret.max_height = NF_MAX_HEIGHT; 269 | ret.ppi_x = ppi_x; 270 | ret.ppi_y = ppi_y; 271 | return ret; 272 | } 273 | 274 | nf_font_t nf_font(const char * font_name, nf_font_params_t params) 275 | { 276 | if(nf_ctx_init() < 0) 277 | return 0; 278 | 279 | if(!font_name) 280 | font_name = "Segoe UI"; 281 | 282 | size_t len = strlen(font_name) + 1; 283 | WCHAR * wtext = (WCHAR*)alloca(len * sizeof(WCHAR)); 284 | size_t wlen = mbstowcs(wtext, font_name, len); 285 | if(wlen == (size_t)-1) 286 | { 287 | NF_ERROR("failed to convert text to wchar, text : '%s'\n", font_name); 288 | return 0; 289 | } 290 | 291 | DWRITE_FONT_WEIGHT weight = DWRITE_FONT_WEIGHT_NORMAL; 292 | switch(params.weight) 293 | { 294 | case NF_WEIGHT_NORMAL: 295 | weight = DWRITE_FONT_WEIGHT_NORMAL; 296 | break; 297 | case NF_WEIGHT_BOLD: 298 | weight = DWRITE_FONT_WEIGHT_BOLD; 299 | break; 300 | case NF_WEIGHT_LIGHT: 301 | weight = DWRITE_FONT_WEIGHT_LIGHT; 302 | break; 303 | default: 304 | break; 305 | } 306 | 307 | DWRITE_FONT_STYLE style = DWRITE_FONT_STYLE_NORMAL; 308 | switch(params.style) 309 | { 310 | case NF_STYLE_NORMAL: 311 | style = DWRITE_FONT_STYLE_NORMAL; 312 | break; 313 | case NF_STYLE_OBLIQUE: 314 | style = DWRITE_FONT_STYLE_OBLIQUE; 315 | break; 316 | case NF_STYLE_ITALIC: 317 | style = DWRITE_FONT_STYLE_ITALIC; 318 | break; 319 | default: 320 | break; 321 | } 322 | 323 | DWRITE_FONT_STRETCH stretch = DWRITE_FONT_STRETCH_NORMAL; 324 | switch(params.stretch) 325 | { 326 | case NF_STRETCH_NORMAL: 327 | stretch = DWRITE_FONT_STRETCH_NORMAL; 328 | break; 329 | case NF_STRETCH_EXPANDED: 330 | stretch = DWRITE_FONT_STRETCH_EXPANDED; 331 | break; 332 | case NF_STRETCH_CONDENSED: 333 | stretch = DWRITE_FONT_STRETCH_CONDENSED; 334 | break; 335 | default: 336 | break; 337 | } 338 | 339 | IDWriteTextFormat * format = NULL; 340 | HRESULT hr = 0; 341 | if(FAILED(hr = ctx.dw_factory->CreateTextFormat( 342 | wtext, 343 | NULL, 344 | weight, 345 | style, 346 | stretch, 347 | params.size_in_pt, // TODO maybe pt / 72 * 96 ? 348 | L"", // en-us ? 349 | &format))) 350 | { 351 | nf_explain_hr(hr, "can't create text format"); 352 | return -1; 353 | } 354 | 355 | return (uintptr_t)format; 356 | } 357 | 358 | void nf_free(nf_font_t font) 359 | { 360 | if(font) 361 | { 362 | IDWriteTextFormat * format = (IDWriteTextFormat*)font; 363 | format->Release(); 364 | } 365 | 366 | nf_ctx_free(); 367 | } 368 | 369 | int nf_print(void * bitmap, uint16_t w, uint16_t h, 370 | nf_font_t font, nf_feature_t * features, size_t features_count, 371 | nf_aabb_t * result_rect, const char * text) 372 | { 373 | if(!bitmap) 374 | { 375 | NF_ERROR("can't print with invalid bitmap\n"); 376 | return -1; 377 | } 378 | 379 | if(!font) 380 | { 381 | NF_ERROR("can't print with invalid font\n"); 382 | return -1; 383 | } 384 | 385 | if(!text) 386 | { 387 | NF_ERROR("can't print with invalid text\n"); 388 | return -1; 389 | } 390 | 391 | // figure out text rendering settings 392 | HRESULT hr = 0; 393 | D2D1_COLOR_F bg_color = D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f); 394 | D2D1_COLOR_F fg_color = D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f); 395 | DWRITE_WORD_WRAPPING text_wrap = DWRITE_WORD_WRAPPING_WRAP; 396 | DWRITE_TEXT_ALIGNMENT text_alignment = DWRITE_TEXT_ALIGNMENT_LEADING; 397 | DWRITE_PARAGRAPH_ALIGNMENT parg_alignment = DWRITE_PARAGRAPH_ALIGNMENT_NEAR; 398 | D2D1_TEXT_ANTIALIAS_MODE text_aa_mode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; 399 | float ppi_x = 0.0f, ppi_y = 0.0f; 400 | 401 | ctx.d2d_factory->GetDesktopDpi(&ppi_x, &ppi_y); 402 | 403 | size_t len = strlen(text) + 1; 404 | WCHAR * wtext = (WCHAR*)alloca(len * sizeof(WCHAR)); 405 | size_t wlen = mbstowcs(wtext, text, len); 406 | if(wlen == (size_t)-1) 407 | { 408 | NF_ERROR("failed to convert text to wchar, text : '%s'\n", text); 409 | return -1; 410 | } 411 | 412 | IDWriteTextLayout * layout = NULL; 413 | if(FAILED(hr = ctx.dw_factory->CreateTextLayout( 414 | wtext, wlen, 415 | (IDWriteTextFormat*)font, 416 | w, h, 417 | &layout))) 418 | { 419 | nf_explain_hr(hr, "can't create dwrite text layout"); 420 | return -1; 421 | } 422 | 423 | for(size_t i = 0; i < features_count; ++i) 424 | { 425 | DWRITE_TEXT_RANGE range; 426 | range.startPosition = features[i].range.start; 427 | range.length = features[i].range.end - features[i].range.start + 1; 428 | 429 | switch(features[i].type) 430 | { 431 | case NF_FEATURE_BOLD: 432 | layout->SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, range); 433 | break; 434 | case NF_FEATURE_UNDERLINE: 435 | layout->SetUnderline(true, range); 436 | break; 437 | case NF_FEATURE_ITALIC: 438 | layout->SetFontStyle(DWRITE_FONT_STYLE_ITALIC, range); 439 | break; 440 | case NF_FEATURE_WRAP: 441 | text_wrap = DWRITE_WORD_WRAPPING_WRAP; 442 | break; 443 | case NF_FEATURE_NO_WRAP: 444 | text_wrap = DWRITE_WORD_WRAPPING_NO_WRAP; 445 | break; 446 | case NF_FEATURE_ALIGN_LEFT: 447 | text_alignment = DWRITE_TEXT_ALIGNMENT_LEADING; 448 | break; 449 | case NF_FEATURE_ALIGN_CENTER: 450 | text_alignment = DWRITE_TEXT_ALIGNMENT_CENTER; 451 | break; 452 | case NF_FEATURE_ALIGN_RIGHT: 453 | text_alignment = DWRITE_TEXT_ALIGNMENT_TRAILING; 454 | break; 455 | case NF_FEATURE_ALIGN_JUSTIFIED: 456 | text_alignment = DWRITE_TEXT_ALIGNMENT_JUSTIFIED; 457 | break; 458 | case NF_FEATURE_ALIGN_PARAGRAPH_LEFT: 459 | parg_alignment = DWRITE_PARAGRAPH_ALIGNMENT_NEAR; 460 | break; 461 | case NF_FEATURE_ALIGN_PARAGRAPH_CENTER: 462 | parg_alignment = DWRITE_PARAGRAPH_ALIGNMENT_CENTER; 463 | break; 464 | case NF_FEATURE_ALIGN_PARAGRAPH_RIGHT: 465 | parg_alignment = DWRITE_PARAGRAPH_ALIGNMENT_FAR; 466 | break; 467 | case NF_FEATURE_AA_DISABLED: 468 | text_aa_mode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; 469 | break; 470 | case NF_FEATURE_AA_WIN_CLEARTYPE: 471 | text_aa_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; 472 | break; 473 | case NF_FEATURE_AA_WIN_GREYSCALE: 474 | text_aa_mode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; 475 | break; 476 | case NF_FEATURE_PPI: 477 | ppi_x = features[i].ppi.x; 478 | ppi_y = features[i].ppi.y; 479 | break; 480 | case NF_FEATURE_COLOR_BG: 481 | bg_color = D2D1::ColorF( 482 | features[i].color.r, 483 | features[i].color.g, 484 | features[i].color.b, 485 | features[i].color.a); 486 | break; 487 | case NF_FEATURE_COLOR_TEXT: 488 | fg_color = D2D1::ColorF( 489 | features[i].color.r, 490 | features[i].color.g, 491 | features[i].color.b, 492 | features[i].color.a); 493 | break; 494 | default: 495 | break; 496 | } 497 | } 498 | 499 | layout->SetWordWrapping(text_wrap); 500 | layout->SetTextAlignment(text_alignment); 501 | layout->SetParagraphAlignment(parg_alignment); 502 | ctx.d2d_rt->SetDpi(ppi_x, ppi_y); 503 | ctx.d2d_rt->SetTextAntialiasMode(text_aa_mode); 504 | ctx.d2d_brush->SetColor(fg_color); 505 | 506 | // figure our result metrics 507 | // TODO does this call actually rasterizes text? 508 | DWRITE_TEXT_METRICS text_metrics; 509 | layout->GetMetrics(&text_metrics); 510 | float clip_x1 = text_metrics.left; 511 | float clip_y1 = text_metrics.top; 512 | float clip_x2 = text_metrics.left + text_metrics.width; 513 | float clip_y2 = text_metrics.top + text_metrics.height; 514 | clip_x1 = clip_x1 < 0 ? 0 : (clip_x1 >= w ? w - 1 : clip_x1); 515 | clip_y1 = clip_y1 < 0 ? 0 : (clip_y1 >= h ? h - 1 : clip_y1); 516 | clip_x2 = clip_x2 < 0 ? 0 : (clip_x2 >= w ? w - 1 : clip_x2); 517 | clip_y2 = clip_y2 < 0 ? 0 : (clip_y2 >= h ? h - 1 : clip_y2); 518 | float clip_w = clip_x2 - clip_x1 + 1.0f; 519 | float clip_h = clip_y2 - clip_y1 + 1.0f; 520 | nf_aabb_t aabb; 521 | aabb.x = clip_x1; 522 | aabb.y = clip_y1; 523 | aabb.w = clip_w; 524 | aabb.h = clip_h; 525 | if(result_rect) 526 | *result_rect = aabb; 527 | 528 | // render text 529 | ctx.d2d_rt->BeginDraw(); 530 | ctx.d2d_rt->Clear(bg_color); 531 | ctx.d2d_rt->DrawTextLayout(D2D1::Point2F(), layout, ctx.d2d_brush); 532 | ctx.d2d_rt->EndDraw(); 533 | layout->Release(); 534 | layout = NULL; 535 | 536 | // read texture from d3d 537 | ctx.d3d_device->CopyResource(ctx.d3d_texture2, ctx.d3d_texture1); 538 | 539 | D3D10_MAPPED_TEXTURE2D mapped = {0}; 540 | if(FAILED(hr = ctx.d3d_texture2->Map(0, D3D10_MAP_READ, 0, &mapped))) 541 | { 542 | nf_explain_hr(hr, "can't map d3d texture"); 543 | return -1; 544 | } 545 | 546 | for(size_t j = aabb.y; j < aabb.y + aabb.h; ++j) 547 | // hardcoded BGRA8 format 548 | memcpy( 549 | (uint8_t*)bitmap + (j * w + aabb.x) * 4, 550 | (uint8_t*)mapped.pData + j * mapped.RowPitch + aabb.x, 551 | aabb.w * 4); 552 | 553 | ctx.d3d_texture2->Unmap(0); 554 | return 0; 555 | } 556 | 557 | #endif 558 | --------------------------------------------------------------------------------