Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/source/fitz/draw-glyph.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 #include "mupdf/fitz.h" | |
| 24 #include "draw-imp.h" | |
| 25 #include "glyph-imp.h" | |
| 26 #include "pixmap-imp.h" | |
| 27 | |
| 28 #include <string.h> | |
| 29 #include <math.h> | |
| 30 | |
| 31 #define MAX_GLYPH_SIZE 256 | |
| 32 #define MAX_CACHE_SIZE (1024*1024) | |
| 33 | |
| 34 #define GLYPH_HASH_LEN 509 | |
| 35 | |
| 36 typedef struct | |
| 37 { | |
| 38 fz_font *font; | |
| 39 int a, b; | |
| 40 int c, d; | |
| 41 unsigned short gid; | |
| 42 unsigned char e, f; | |
| 43 int aa; | |
| 44 } fz_glyph_key; | |
| 45 | |
| 46 typedef struct fz_glyph_cache_entry | |
| 47 { | |
| 48 fz_glyph_key key; | |
| 49 unsigned hash; | |
| 50 struct fz_glyph_cache_entry *lru_prev; | |
| 51 struct fz_glyph_cache_entry *lru_next; | |
| 52 struct fz_glyph_cache_entry *bucket_next; | |
| 53 struct fz_glyph_cache_entry *bucket_prev; | |
| 54 fz_glyph *val; | |
| 55 } fz_glyph_cache_entry; | |
| 56 | |
| 57 struct fz_glyph_cache | |
| 58 { | |
| 59 int refs; | |
| 60 size_t total; | |
| 61 #ifndef NDEBUG | |
| 62 int num_evictions; | |
| 63 ptrdiff_t evicted; | |
| 64 #endif | |
| 65 fz_glyph_cache_entry *entry[GLYPH_HASH_LEN]; | |
| 66 fz_glyph_cache_entry *lru_head; | |
| 67 fz_glyph_cache_entry *lru_tail; | |
| 68 }; | |
| 69 | |
| 70 static size_t | |
| 71 fz_glyph_size(fz_context *ctx, fz_glyph *glyph) | |
| 72 { | |
| 73 if (glyph == NULL) | |
| 74 return 0; | |
| 75 return sizeof(fz_glyph) + glyph->size + fz_pixmap_size(ctx, glyph->pixmap); | |
| 76 } | |
| 77 | |
| 78 void | |
| 79 fz_new_glyph_cache_context(fz_context *ctx) | |
| 80 { | |
| 81 fz_glyph_cache *cache; | |
| 82 | |
| 83 cache = fz_malloc_struct(ctx, fz_glyph_cache); | |
| 84 cache->total = 0; | |
| 85 cache->refs = 1; | |
| 86 | |
| 87 ctx->glyph_cache = cache; | |
| 88 } | |
| 89 | |
| 90 static void | |
| 91 drop_glyph_cache_entry(fz_context *ctx, fz_glyph_cache_entry *entry) | |
| 92 { | |
| 93 fz_glyph_cache *cache = ctx->glyph_cache; | |
| 94 | |
| 95 if (entry->lru_next) | |
| 96 entry->lru_next->lru_prev = entry->lru_prev; | |
| 97 else | |
| 98 cache->lru_tail = entry->lru_prev; | |
| 99 if (entry->lru_prev) | |
| 100 entry->lru_prev->lru_next = entry->lru_next; | |
| 101 else | |
| 102 cache->lru_head = entry->lru_next; | |
| 103 cache->total -= fz_glyph_size(ctx, entry->val); | |
| 104 if (entry->bucket_next) | |
| 105 entry->bucket_next->bucket_prev = entry->bucket_prev; | |
| 106 if (entry->bucket_prev) | |
| 107 entry->bucket_prev->bucket_next = entry->bucket_next; | |
| 108 else | |
| 109 cache->entry[entry->hash] = entry->bucket_next; | |
| 110 fz_drop_font(ctx, entry->key.font); | |
| 111 fz_drop_glyph(ctx, entry->val); | |
| 112 fz_free(ctx, entry); | |
| 113 } | |
| 114 | |
| 115 /* The glyph cache lock is always held when this function is called. */ | |
| 116 static void | |
| 117 do_purge(fz_context *ctx) | |
| 118 { | |
| 119 fz_glyph_cache *cache = ctx->glyph_cache; | |
| 120 int i; | |
| 121 | |
| 122 for (i = 0; i < GLYPH_HASH_LEN; i++) | |
| 123 { | |
| 124 while (cache->entry[i]) | |
| 125 drop_glyph_cache_entry(ctx, cache->entry[i]); | |
| 126 } | |
| 127 | |
| 128 cache->total = 0; | |
| 129 } | |
| 130 | |
| 131 void | |
| 132 fz_purge_glyph_cache(fz_context *ctx) | |
| 133 { | |
| 134 fz_lock(ctx, FZ_LOCK_GLYPHCACHE); | |
| 135 do_purge(ctx); | |
| 136 fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); | |
| 137 } | |
| 138 | |
| 139 void | |
| 140 fz_drop_glyph_cache_context(fz_context *ctx) | |
| 141 { | |
| 142 if (!ctx || !ctx->glyph_cache) | |
| 143 return; | |
| 144 | |
| 145 fz_lock(ctx, FZ_LOCK_GLYPHCACHE); | |
| 146 ctx->glyph_cache->refs--; | |
| 147 if (ctx->glyph_cache->refs == 0) | |
| 148 { | |
| 149 do_purge(ctx); | |
| 150 fz_free(ctx, ctx->glyph_cache); | |
| 151 ctx->glyph_cache = NULL; | |
| 152 } | |
| 153 fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); | |
| 154 } | |
| 155 | |
| 156 fz_glyph_cache * | |
| 157 fz_keep_glyph_cache(fz_context *ctx) | |
| 158 { | |
| 159 fz_lock(ctx, FZ_LOCK_GLYPHCACHE); | |
| 160 ctx->glyph_cache->refs++; | |
| 161 fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); | |
| 162 return ctx->glyph_cache; | |
| 163 } | |
| 164 | |
| 165 float | |
| 166 fz_subpixel_adjust(fz_context *ctx, fz_matrix *ctm, fz_matrix *subpix_ctm, unsigned char *qe, unsigned char *qf) | |
| 167 { | |
| 168 float size = fz_matrix_expansion(*ctm); | |
| 169 int q, hq, vq, qmin; | |
| 170 float pix_e, pix_f, r, hr, vr, rmin; | |
| 171 | |
| 172 /* Quantise the subpixel positions. First, in the direction of | |
| 173 * movement (i.e. normally X). We never need more than 4 subpixel | |
| 174 * positions for glyphs - arguably even that is too much. | |
| 175 * Suppress this as we get larger, because it makes less impact. */ | |
| 176 if (size >= 48) | |
| 177 q = 0, r = 0.5f; | |
| 178 else if (size >= 24) | |
| 179 q = 128, r = 0.25f; | |
| 180 else | |
| 181 q = 192, r = 0.125f; | |
| 182 | |
| 183 /* Then in the 'downward' direction (normally Y). */ | |
| 184 if (size >= 8) | |
| 185 qmin = 0, rmin = 0.5f; | |
| 186 else if (size >= 4) | |
| 187 qmin = 128, rmin = 0.25f; | |
| 188 else | |
| 189 qmin = 192, rmin = 0.125f; | |
| 190 | |
| 191 /* Suppress subpixel antialiasing in y axis if we have a horizontal | |
| 192 * matrix, and in x axis if we have a vertical matrix, unless we're | |
| 193 * really small. */ | |
| 194 hq = vq = q; | |
| 195 hr = vr = r; | |
| 196 if (ctm->a == 0 && ctm->d == 0) | |
| 197 hq = qmin, hr = rmin; | |
| 198 if (ctm->b == 0 && ctm->c == 0) | |
| 199 vq = qmin, vr = rmin; | |
| 200 | |
| 201 /* Split translation into pixel and subpixel parts */ | |
| 202 subpix_ctm->a = ctm->a; | |
| 203 subpix_ctm->b = ctm->b; | |
| 204 subpix_ctm->c = ctm->c; | |
| 205 subpix_ctm->d = ctm->d; | |
| 206 subpix_ctm->e = ctm->e + hr; | |
| 207 pix_e = floorf(subpix_ctm->e); | |
| 208 subpix_ctm->e -= pix_e; | |
| 209 subpix_ctm->f = ctm->f + vr; | |
| 210 pix_f = floorf(subpix_ctm->f); | |
| 211 subpix_ctm->f -= pix_f; | |
| 212 | |
| 213 /* Quantise the subpixel part */ | |
| 214 *qe = (int)(subpix_ctm->e * 256) & hq; | |
| 215 subpix_ctm->e = *qe / 256.0f; | |
| 216 *qf = (int)(subpix_ctm->f * 256) & vq; | |
| 217 subpix_ctm->f = *qf / 256.0f; | |
| 218 | |
| 219 /* Reassemble the complete translation */ | |
| 220 ctm->e = subpix_ctm->e + pix_e; | |
| 221 ctm->f = subpix_ctm->f + pix_f; | |
| 222 | |
| 223 return size; | |
| 224 } | |
| 225 | |
| 226 fz_glyph * | |
| 227 fz_render_stroked_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix *trm, fz_matrix ctm, fz_colorspace *model, const fz_stroke_state *stroke, const fz_irect *scissor, int aa) | |
| 228 { | |
| 229 if (fz_font_ft_face(ctx, font)) | |
| 230 { | |
| 231 fz_matrix subpix_trm; | |
| 232 unsigned char qe, qf; | |
| 233 | |
| 234 if (stroke->dash_len > 0) | |
| 235 return NULL; | |
| 236 (void)fz_subpixel_adjust(ctx, trm, &subpix_trm, &qe, &qf); | |
| 237 return fz_render_ft_stroked_glyph(ctx, font, gid, subpix_trm, ctm, stroke, aa); | |
| 238 } | |
| 239 return fz_render_glyph(ctx, font, gid, trm, model, scissor, 1, aa); | |
| 240 } | |
| 241 | |
| 242 static unsigned do_hash(unsigned char *s, int len) | |
| 243 { | |
| 244 unsigned val = 0; | |
| 245 int i; | |
| 246 for (i = 0; i < len; i++) | |
| 247 { | |
| 248 val += s[i]; | |
| 249 val += (val << 10); | |
| 250 val ^= (val >> 6); | |
| 251 } | |
| 252 val += (val << 3); | |
| 253 val ^= (val >> 11); | |
| 254 val += (val << 15); | |
| 255 return val; | |
| 256 } | |
| 257 | |
| 258 static inline void | |
| 259 move_to_front(fz_glyph_cache *cache, fz_glyph_cache_entry *entry) | |
| 260 { | |
| 261 if (entry->lru_prev == NULL) | |
| 262 return; /* At front already */ | |
| 263 | |
| 264 /* Unlink */ | |
| 265 entry->lru_prev->lru_next = entry->lru_next; | |
| 266 if (entry->lru_next) | |
| 267 entry->lru_next->lru_prev = entry->lru_prev; | |
| 268 else | |
| 269 cache->lru_tail = entry->lru_prev; | |
| 270 /* Relink */ | |
| 271 entry->lru_next = cache->lru_head; | |
| 272 if (entry->lru_next) | |
| 273 entry->lru_next->lru_prev = entry; | |
| 274 cache->lru_head = entry; | |
| 275 entry->lru_prev = NULL; | |
| 276 } | |
| 277 | |
| 278 fz_glyph * | |
| 279 fz_render_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix *ctm, fz_colorspace *model, const fz_irect *scissor, int alpha, int aa) | |
| 280 { | |
| 281 fz_glyph_cache *cache; | |
| 282 fz_glyph_key key; | |
| 283 fz_matrix subpix_ctm; | |
| 284 fz_irect subpix_scissor; | |
| 285 float size; | |
| 286 fz_glyph *val; | |
| 287 int do_cache, locked, caching; | |
| 288 fz_glyph_cache_entry *entry; | |
| 289 unsigned hash; | |
| 290 int is_ft_font = !!fz_font_ft_face(ctx, font); | |
| 291 | |
| 292 fz_var(locked); | |
| 293 fz_var(caching); | |
| 294 fz_var(val); | |
| 295 | |
| 296 memset(&key, 0, sizeof key); | |
| 297 size = fz_subpixel_adjust(ctx, ctm, &subpix_ctm, &key.e, &key.f); | |
| 298 if (size <= MAX_GLYPH_SIZE) | |
| 299 { | |
| 300 scissor = &fz_infinite_irect; | |
| 301 do_cache = 1; | |
| 302 } | |
| 303 else | |
| 304 { | |
| 305 if (is_ft_font) | |
| 306 return NULL; | |
| 307 subpix_scissor.x0 = scissor->x0 - floorf(ctm->e); | |
| 308 subpix_scissor.y0 = scissor->y0 - floorf(ctm->f); | |
| 309 subpix_scissor.x1 = scissor->x1 - floorf(ctm->e); | |
| 310 subpix_scissor.y1 = scissor->y1 - floorf(ctm->f); | |
| 311 scissor = &subpix_scissor; | |
| 312 do_cache = 0; | |
| 313 } | |
| 314 | |
| 315 cache = ctx->glyph_cache; | |
| 316 | |
| 317 key.font = font; | |
| 318 key.gid = gid; | |
| 319 key.a = subpix_ctm.a * 65536; | |
| 320 key.b = subpix_ctm.b * 65536; | |
| 321 key.c = subpix_ctm.c * 65536; | |
| 322 key.d = subpix_ctm.d * 65536; | |
| 323 key.aa = aa; | |
| 324 | |
| 325 hash = do_hash((unsigned char *)&key, sizeof(key)) % GLYPH_HASH_LEN; | |
| 326 fz_lock(ctx, FZ_LOCK_GLYPHCACHE); | |
| 327 entry = cache->entry[hash]; | |
| 328 while (entry) | |
| 329 { | |
| 330 if (memcmp(&entry->key, &key, sizeof(key)) == 0) | |
| 331 { | |
| 332 move_to_front(cache, entry); | |
| 333 val = fz_keep_glyph(ctx, entry->val); | |
| 334 fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); | |
| 335 return val; | |
| 336 } | |
| 337 entry = entry->bucket_next; | |
| 338 } | |
| 339 | |
| 340 locked = 1; | |
| 341 caching = 0; | |
| 342 val = NULL; | |
| 343 | |
| 344 fz_try(ctx) | |
| 345 { | |
| 346 if (is_ft_font) | |
| 347 { | |
| 348 val = fz_render_ft_glyph(ctx, font, gid, subpix_ctm, aa); | |
| 349 } | |
| 350 else if (fz_font_t3_procs(ctx, font)) | |
| 351 { | |
| 352 /* We drop the glyphcache here, and execute the t3 | |
| 353 * glyph code. The danger here is that some other | |
| 354 * thread will come along, and want the same glyph | |
| 355 * too. If it does, we may both end up rendering | |
| 356 * pixmaps. We cope with this later on, by ensuring | |
| 357 * that only one gets inserted into the cache. If | |
| 358 * we insert ours to find one already there, we | |
| 359 * abandon ours, and use the one there already. | |
| 360 */ | |
| 361 fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); | |
| 362 locked = 0; | |
| 363 val = fz_render_t3_glyph(ctx, font, gid, subpix_ctm, model, scissor, aa); | |
| 364 fz_lock(ctx, FZ_LOCK_GLYPHCACHE); | |
| 365 locked = 1; | |
| 366 } | |
| 367 else | |
| 368 { | |
| 369 fz_warn(ctx, "assert: uninitialized font structure"); | |
| 370 } | |
| 371 if (val && do_cache) | |
| 372 { | |
| 373 if (val->w < MAX_GLYPH_SIZE && val->h < MAX_GLYPH_SIZE) | |
| 374 { | |
| 375 /* If we throw an exception whilst caching, | |
| 376 * just ignore the exception and carry on. */ | |
| 377 caching = 1; | |
| 378 if (!is_ft_font) | |
| 379 { | |
| 380 /* We had to unlock. Someone else might | |
| 381 * have rendered in the meantime */ | |
| 382 entry = cache->entry[hash]; | |
| 383 while (entry) | |
| 384 { | |
| 385 if (memcmp(&entry->key, &key, sizeof(key)) == 0) | |
| 386 { | |
| 387 fz_drop_glyph(ctx, val); | |
| 388 move_to_front(cache, entry); | |
| 389 val = fz_keep_glyph(ctx, entry->val); | |
| 390 goto unlock_and_return_val; | |
| 391 } | |
| 392 entry = entry->bucket_next; | |
| 393 } | |
| 394 } | |
| 395 | |
| 396 entry = fz_malloc_struct(ctx, fz_glyph_cache_entry); | |
| 397 entry->key = key; | |
| 398 entry->hash = hash; | |
| 399 entry->bucket_next = cache->entry[hash]; | |
| 400 if (entry->bucket_next) | |
| 401 entry->bucket_next->bucket_prev = entry; | |
| 402 cache->entry[hash] = entry; | |
| 403 entry->val = fz_keep_glyph(ctx, val); | |
| 404 fz_keep_font(ctx, key.font); | |
| 405 | |
| 406 entry->lru_next = cache->lru_head; | |
| 407 if (entry->lru_next) | |
| 408 entry->lru_next->lru_prev = entry; | |
| 409 else | |
| 410 cache->lru_tail = entry; | |
| 411 cache->lru_head = entry; | |
| 412 | |
| 413 cache->total += fz_glyph_size(ctx, val); | |
| 414 while (cache->total > MAX_CACHE_SIZE) | |
| 415 { | |
| 416 #ifndef NDEBUG | |
| 417 cache->num_evictions++; | |
| 418 cache->evicted += fz_glyph_size(ctx, cache->lru_tail->val); | |
| 419 #endif | |
| 420 drop_glyph_cache_entry(ctx, cache->lru_tail); | |
| 421 } | |
| 422 } | |
| 423 } | |
| 424 unlock_and_return_val: | |
| 425 { | |
| 426 } | |
| 427 } | |
| 428 fz_always(ctx) | |
| 429 { | |
| 430 if (locked) | |
| 431 fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); | |
| 432 } | |
| 433 fz_catch(ctx) | |
| 434 { | |
| 435 if (caching) | |
| 436 fz_warn(ctx, "cannot encache glyph; continuing"); | |
| 437 else | |
| 438 fz_rethrow(ctx); | |
| 439 } | |
| 440 | |
| 441 return val; | |
| 442 } | |
| 443 | |
| 444 fz_pixmap * | |
| 445 fz_render_glyph_pixmap(fz_context *ctx, fz_font *font, int gid, fz_matrix *ctm, const fz_irect *scissor, int aa) | |
| 446 { | |
| 447 fz_pixmap *val = NULL; | |
| 448 unsigned char qe, qf; | |
| 449 fz_matrix subpix_ctm; | |
| 450 float size = fz_subpixel_adjust(ctx, ctm, &subpix_ctm, &qe, &qf); | |
| 451 int is_ft_font = !!fz_font_ft_face(ctx, font); | |
| 452 | |
| 453 if (size <= MAX_GLYPH_SIZE) | |
| 454 { | |
| 455 scissor = &fz_infinite_irect; | |
| 456 } | |
| 457 else | |
| 458 { | |
| 459 if (is_ft_font) | |
| 460 return NULL; | |
| 461 } | |
| 462 | |
| 463 if (is_ft_font) | |
| 464 { | |
| 465 val = fz_render_ft_glyph_pixmap(ctx, font, gid, subpix_ctm, aa); | |
| 466 } | |
| 467 else if (fz_font_t3_procs(ctx, font)) | |
| 468 { | |
| 469 val = fz_render_t3_glyph_pixmap(ctx, font, gid, subpix_ctm, NULL, scissor, aa); | |
| 470 } | |
| 471 else | |
| 472 { | |
| 473 fz_warn(ctx, "assert: uninitialized font structure"); | |
| 474 val = NULL; | |
| 475 } | |
| 476 | |
| 477 return val; | |
| 478 } | |
| 479 | |
| 480 void | |
| 481 fz_dump_glyph_cache_stats(fz_context *ctx, fz_output *out) | |
| 482 { | |
| 483 fz_glyph_cache *cache = ctx->glyph_cache; | |
| 484 fz_write_printf(ctx, out, "Glyph Cache Size: %zu\n", cache->total); | |
| 485 #ifndef NDEBUG | |
| 486 fz_write_printf(ctx, out, "Glyph Cache Evictions: %d (%zu bytes)\n", cache->num_evictions, cache->evicted); | |
| 487 #endif | |
| 488 } |
