Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/platform/gl/gl-font.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 // Copyright (C) 2004-2021 Artifex Software, Inc. | |
| 2 // | |
| 3 // This file is part of MuPDF. | |
| 4 // | |
| 5 // MuPDF is free software: you can redistribute it and/or modify it under the | |
| 6 // terms of the GNU Affero General Public License as published by the Free | |
| 7 // Software Foundation, either version 3 of the License, or (at your option) | |
| 8 // any later version. | |
| 9 // | |
| 10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| 12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
| 13 // details. | |
| 14 // | |
| 15 // You should have received a copy of the GNU Affero General Public License | |
| 16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> | |
| 17 // | |
| 18 // Alternative licensing terms are available from the licensor. | |
| 19 // For commercial licensing, see <https://www.artifex.com/> or contact | |
| 20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 21 // CA 94129, USA, for further information. | |
| 22 | |
| 23 /* | |
| 24 * A very simple font cache and rasterizer that uses FreeType | |
| 25 * to draw fonts from a single OpenGL texture. The code uses | |
| 26 * a linear-probe hashtable, and writes new glyphs into | |
| 27 * the texture using glTexSubImage2D. When the texture fills | |
| 28 * up, or the hash table gets too crowded, the cache is emptied. | |
| 29 * | |
| 30 * This is designed to be used for horizontal text only, | |
| 31 * and draws unhinted text with subpixel accurate metrics | |
| 32 * and kerning. As such, you should always call the drawing | |
| 33 * function with an orthogonal transform that maps units | |
| 34 * to pixels accurately. | |
| 35 */ | |
| 36 | |
| 37 #include "gl-app.h" | |
| 38 | |
| 39 #include <string.h> | |
| 40 #include <math.h> | |
| 41 #include <stdlib.h> | |
| 42 #include <stdio.h> | |
| 43 | |
| 44 #define PADDING 1 /* set to 0 to save some space but disallow arbitrary transforms */ | |
| 45 | |
| 46 #define MAXGLYPHS 4093 /* prime number for hash table goodness */ | |
| 47 #define CACHESIZE 1024 | |
| 48 #define XPRECISION 4 | |
| 49 #define YPRECISION 1 | |
| 50 | |
| 51 struct key | |
| 52 { | |
| 53 fz_font *font; | |
| 54 float size; | |
| 55 short gid; | |
| 56 unsigned char subx; | |
| 57 unsigned char suby; | |
| 58 }; | |
| 59 | |
| 60 struct glyph | |
| 61 { | |
| 62 char lsb, top, w, h; | |
| 63 short s, t; | |
| 64 }; | |
| 65 | |
| 66 struct table | |
| 67 { | |
| 68 struct key key; | |
| 69 struct glyph glyph; | |
| 70 }; | |
| 71 | |
| 72 static struct table g_table[MAXGLYPHS]; | |
| 73 static int g_table_load = 0; | |
| 74 static unsigned int g_cache_tex = 0; | |
| 75 static int g_cache_w = CACHESIZE; | |
| 76 static int g_cache_h = CACHESIZE; | |
| 77 static int g_cache_row_y = 0; | |
| 78 static int g_cache_row_x = 0; | |
| 79 static int g_cache_row_h = 0; | |
| 80 | |
| 81 static fz_font *g_font = NULL; | |
| 82 | |
| 83 static void clear_font_cache(void) | |
| 84 { | |
| 85 #if PADDING > 0 | |
| 86 unsigned char *zero = malloc((size_t)g_cache_w * g_cache_h); | |
| 87 memset(zero, 0, (size_t)g_cache_w * g_cache_h); | |
| 88 glBindTexture(GL_TEXTURE_2D, g_cache_tex); | |
| 89 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, g_cache_w, g_cache_h, GL_ALPHA, GL_UNSIGNED_BYTE, zero); | |
| 90 free(zero); | |
| 91 #endif | |
| 92 | |
| 93 memset(g_table, 0, sizeof(g_table)); | |
| 94 g_table_load = 0; | |
| 95 | |
| 96 g_cache_row_y = PADDING; | |
| 97 g_cache_row_x = PADDING; | |
| 98 g_cache_row_h = 0; | |
| 99 } | |
| 100 | |
| 101 void ui_init_fonts(void) | |
| 102 { | |
| 103 const unsigned char *data; | |
| 104 int size; | |
| 105 | |
| 106 glGenTextures(1, &g_cache_tex); | |
| 107 glBindTexture(GL_TEXTURE_2D, g_cache_tex); | |
| 108 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| 109 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| 110 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); | |
| 111 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); | |
| 112 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, g_cache_w, g_cache_h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL); | |
| 113 | |
| 114 clear_font_cache(); | |
| 115 | |
| 116 data = fz_lookup_builtin_font(ctx, "Charis SIL", 0, 0, &size); | |
| 117 if (!data) | |
| 118 data = fz_lookup_builtin_font(ctx, "Times", 0, 0, &size); | |
| 119 g_font = fz_new_font_from_memory(ctx, NULL, data, size, 0, 0); | |
| 120 } | |
| 121 | |
| 122 void ui_finish_fonts(void) | |
| 123 { | |
| 124 clear_font_cache(); | |
| 125 fz_drop_font(ctx, g_font); | |
| 126 } | |
| 127 | |
| 128 static unsigned int hashfunc(struct key *key) | |
| 129 { | |
| 130 unsigned char *buf = (unsigned char *)key; | |
| 131 unsigned int len = sizeof(struct key); | |
| 132 unsigned int h = 0; | |
| 133 while (len--) | |
| 134 h = *buf++ + (h << 6) + (h << 16) - h; | |
| 135 return h; | |
| 136 } | |
| 137 | |
| 138 static unsigned int lookup_table(struct key *key) | |
| 139 { | |
| 140 unsigned int pos = hashfunc(key) % MAXGLYPHS; | |
| 141 while (1) | |
| 142 { | |
| 143 if (!g_table[pos].key.font) /* empty slot */ | |
| 144 return pos; | |
| 145 if (!memcmp(key, &g_table[pos].key, sizeof(struct key))) /* matching slot */ | |
| 146 return pos; | |
| 147 pos = (pos + 1) % MAXGLYPHS; | |
| 148 } | |
| 149 } | |
| 150 | |
| 151 static struct glyph *lookup_glyph(fz_font *font, float size, int gid, float *xp, float *yp) | |
| 152 { | |
| 153 fz_matrix trm, subpix_trm; | |
| 154 unsigned char subx, suby; | |
| 155 fz_pixmap *pixmap; | |
| 156 struct key key; | |
| 157 unsigned int pos; | |
| 158 int w, h; | |
| 159 | |
| 160 /* match fitz's glyph cache quantization */ | |
| 161 trm = fz_scale(size, -size); | |
| 162 trm.e = *xp; | |
| 163 trm.f = *yp; | |
| 164 fz_subpixel_adjust(ctx, &trm, &subpix_trm, &subx, &suby); | |
| 165 *xp = trm.e; | |
| 166 *yp = trm.f; | |
| 167 | |
| 168 /* | |
| 169 * Look it up in the table | |
| 170 */ | |
| 171 | |
| 172 memset(&key, 0, sizeof key); | |
| 173 key.font = font; | |
| 174 key.size = size; | |
| 175 key.gid = gid; | |
| 176 key.subx = subx; | |
| 177 key.suby = suby; | |
| 178 | |
| 179 pos = lookup_table(&key); | |
| 180 if (g_table[pos].key.font) | |
| 181 return &g_table[pos].glyph; | |
| 182 | |
| 183 /* | |
| 184 * Render the bitmap | |
| 185 */ | |
| 186 | |
| 187 glEnd(); | |
| 188 | |
| 189 pixmap = fz_render_glyph_pixmap(ctx, font, gid, &subpix_trm, NULL, 8); | |
| 190 w = pixmap->w; | |
| 191 h = pixmap->h; | |
| 192 | |
| 193 /* | |
| 194 * Find an empty slot in the texture | |
| 195 */ | |
| 196 | |
| 197 if (g_table_load == (MAXGLYPHS * 3) / 4) | |
| 198 { | |
| 199 puts("font cache table full, clearing cache"); | |
| 200 clear_font_cache(); | |
| 201 pos = lookup_table(&key); | |
| 202 } | |
| 203 | |
| 204 if (h + PADDING > g_cache_h || w + PADDING > g_cache_w) | |
| 205 return NULL; | |
| 206 | |
| 207 if (g_cache_row_x + w + PADDING > g_cache_w) | |
| 208 { | |
| 209 g_cache_row_y += g_cache_row_h + PADDING; | |
| 210 g_cache_row_x = PADDING; | |
| 211 g_cache_row_h = 0; | |
| 212 } | |
| 213 if (g_cache_row_y + h + PADDING > g_cache_h) | |
| 214 { | |
| 215 puts("font cache texture full, clearing cache"); | |
| 216 clear_font_cache(); | |
| 217 pos = lookup_table(&key); | |
| 218 } | |
| 219 | |
| 220 /* | |
| 221 * Copy bitmap into texture | |
| 222 */ | |
| 223 | |
| 224 memcpy(&g_table[pos].key, &key, sizeof(struct key)); | |
| 225 g_table[pos].glyph.w = pixmap->w; | |
| 226 g_table[pos].glyph.h = pixmap->h; | |
| 227 g_table[pos].glyph.lsb = pixmap->x; | |
| 228 g_table[pos].glyph.top = -pixmap->y; | |
| 229 g_table[pos].glyph.s = g_cache_row_x; | |
| 230 g_table[pos].glyph.t = g_cache_row_y; | |
| 231 g_table_load ++; | |
| 232 | |
| 233 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |
| 234 glPixelStorei(GL_UNPACK_ROW_LENGTH, pixmap->w); | |
| 235 glTexSubImage2D(GL_TEXTURE_2D, 0, g_cache_row_x, g_cache_row_y, w, h, | |
| 236 GL_ALPHA, GL_UNSIGNED_BYTE, pixmap->samples); | |
| 237 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | |
| 238 | |
| 239 fz_drop_pixmap(ctx, pixmap); | |
| 240 | |
| 241 glBegin(GL_QUADS); | |
| 242 | |
| 243 g_cache_row_x += w + PADDING; | |
| 244 if (g_cache_row_h < h + PADDING) | |
| 245 g_cache_row_h = h + PADDING; | |
| 246 | |
| 247 return &g_table[pos].glyph; | |
| 248 } | |
| 249 | |
| 250 static float ui_draw_glyph(fz_font *font, float size, int gid, float x, float y) | |
| 251 { | |
| 252 struct glyph *glyph; | |
| 253 float s0, t0, s1, t1, xc, yc; | |
| 254 | |
| 255 glyph = lookup_glyph(font, size, gid, &x, &y); | |
| 256 if (!glyph) | |
| 257 return 0; | |
| 258 | |
| 259 s0 = (float) glyph->s / g_cache_w; | |
| 260 t0 = (float) glyph->t / g_cache_h; | |
| 261 s1 = (float) (glyph->s + glyph->w) / g_cache_w; | |
| 262 t1 = (float) (glyph->t + glyph->h) / g_cache_h; | |
| 263 xc = floorf(x) + glyph->lsb; | |
| 264 yc = floorf(y) - glyph->top + glyph->h; | |
| 265 | |
| 266 glTexCoord2f(s0, t0); glVertex2f(xc, yc - glyph->h); | |
| 267 glTexCoord2f(s1, t0); glVertex2f(xc + glyph->w, yc - glyph->h); | |
| 268 glTexCoord2f(s1, t1); glVertex2f(xc + glyph->w, yc); | |
| 269 glTexCoord2f(s0, t1); glVertex2f(xc, yc); | |
| 270 | |
| 271 return fz_advance_glyph(ctx, font, gid, 0) * size; | |
| 272 } | |
| 273 | |
| 274 float ui_measure_character(int c) | |
| 275 { | |
| 276 fz_font *font; | |
| 277 int gid = fz_encode_character_with_fallback(ctx, g_font, c, 0, 0, &font); | |
| 278 return fz_advance_glyph(ctx, font, gid, 0) * ui.fontsize; | |
| 279 } | |
| 280 | |
| 281 static float ui_draw_character_imp(float x, float y, int c) | |
| 282 { | |
| 283 fz_font *font; | |
| 284 int gid = fz_encode_character_with_fallback(ctx, g_font, c, 0, 0, &font); | |
| 285 return ui_draw_glyph(font, ui.fontsize, gid, x, y); | |
| 286 } | |
| 287 | |
| 288 static void ui_begin_text(void) | |
| 289 { | |
| 290 glBindTexture(GL_TEXTURE_2D, g_cache_tex); | |
| 291 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
| 292 glEnable(GL_BLEND); | |
| 293 glEnable(GL_TEXTURE_2D); | |
| 294 glBegin(GL_QUADS); | |
| 295 } | |
| 296 | |
| 297 static void ui_end_text(void) | |
| 298 { | |
| 299 glEnd(); | |
| 300 glDisable(GL_TEXTURE_2D); | |
| 301 glDisable(GL_BLEND); | |
| 302 } | |
| 303 | |
| 304 void ui_draw_string(float x, float y, const char *str) | |
| 305 { | |
| 306 int c; | |
| 307 ui_begin_text(); | |
| 308 while (*str) | |
| 309 { | |
| 310 str += fz_chartorune(&c, str); | |
| 311 x += ui_draw_character_imp(x, y + ui.baseline, c); | |
| 312 } | |
| 313 ui_end_text(); | |
| 314 } | |
| 315 | |
| 316 void ui_draw_string_part(float x, float y, const char *s, const char *e) | |
| 317 { | |
| 318 int c; | |
| 319 ui_begin_text(); | |
| 320 while (s < e) | |
| 321 { | |
| 322 s += fz_chartorune(&c, s); | |
| 323 x += ui_draw_character_imp(x, y + ui.baseline, c); | |
| 324 } | |
| 325 ui_end_text(); | |
| 326 } | |
| 327 | |
| 328 void ui_draw_character(float x, float y, int c) | |
| 329 { | |
| 330 ui_begin_text(); | |
| 331 ui_draw_character_imp(x, y + ui.baseline, c); | |
| 332 ui_end_text(); | |
| 333 } | |
| 334 | |
| 335 float ui_measure_string(const char *str) | |
| 336 { | |
| 337 int c; | |
| 338 float x = 0; | |
| 339 while (*str) | |
| 340 { | |
| 341 str += fz_chartorune(&c, str); | |
| 342 x += ui_measure_character(c); | |
| 343 } | |
| 344 return x; | |
| 345 } | |
| 346 | |
| 347 float ui_measure_string_part(const char *s, const char *e) | |
| 348 { | |
| 349 int c; | |
| 350 float w = 0; | |
| 351 while (s < e) | |
| 352 { | |
| 353 s += fz_chartorune(&c, s); | |
| 354 w += ui_measure_character(c); | |
| 355 } | |
| 356 return w; | |
| 357 } | |
| 358 | |
| 359 int ui_break_lines(char *a, struct line *lines, int maxlines, int width, int *maxwidth) | |
| 360 { | |
| 361 char *next, *space = NULL, *b = a; | |
| 362 int c, n = 0; | |
| 363 float space_x, x = 0, w = 0; | |
| 364 | |
| 365 if (maxwidth) | |
| 366 *maxwidth = 0; | |
| 367 | |
| 368 while (*b) | |
| 369 { | |
| 370 next = b + fz_chartorune(&c, b); | |
| 371 if (c == '\r' || c == '\n') | |
| 372 { | |
| 373 if (lines && n < maxlines) | |
| 374 { | |
| 375 lines[n].a = a; | |
| 376 lines[n].b = b; | |
| 377 } | |
| 378 ++n; | |
| 379 if (maxwidth && *maxwidth < x) | |
| 380 *maxwidth = x; | |
| 381 a = next; | |
| 382 x = 0; | |
| 383 space = NULL; | |
| 384 } | |
| 385 else | |
| 386 { | |
| 387 if (c == ' ' && maxlines > 1) | |
| 388 { | |
| 389 space = b; | |
| 390 space_x = x; | |
| 391 } | |
| 392 | |
| 393 w = ui_measure_character(c); | |
| 394 if (x + w > width) | |
| 395 { | |
| 396 if (space) | |
| 397 { | |
| 398 if (lines && n < maxlines) | |
| 399 { | |
| 400 lines[n].a = a; | |
| 401 lines[n].b = space; | |
| 402 } | |
| 403 ++n; | |
| 404 if (maxwidth && *maxwidth < space_x) | |
| 405 *maxwidth = space_x; | |
| 406 a = next = space + 1; | |
| 407 x = 0; | |
| 408 space = NULL; | |
| 409 } | |
| 410 else | |
| 411 { | |
| 412 if (lines && n < maxlines) | |
| 413 { | |
| 414 lines[n].a = a; | |
| 415 lines[n].b = b; | |
| 416 } | |
| 417 ++n; | |
| 418 if (maxwidth && *maxwidth < x) | |
| 419 *maxwidth = x; | |
| 420 a = b; | |
| 421 x = w; | |
| 422 space = NULL; | |
| 423 } | |
| 424 } | |
| 425 else | |
| 426 { | |
| 427 x += w; | |
| 428 } | |
| 429 } | |
| 430 b = next; | |
| 431 } | |
| 432 | |
| 433 if (lines && n < maxlines) | |
| 434 { | |
| 435 lines[n].a = a; | |
| 436 lines[n].b = b; | |
| 437 } | |
| 438 ++n; | |
| 439 if (maxwidth && *maxwidth < x) | |
| 440 *maxwidth = x; | |
| 441 return n < maxlines ? n : maxlines; | |
| 442 } | |
| 443 | |
| 444 void ui_draw_lines(float x, float y, struct line *lines, int n) | |
| 445 { | |
| 446 int i; | |
| 447 for (i = 0; i < n; ++i) | |
| 448 { | |
| 449 ui_draw_string_part(x, y, lines[i].a, lines[i].b); | |
| 450 y += ui.lineheight; | |
| 451 } | |
| 452 } |
