From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Manuel Giraud via "Bug reports for GNU Emacs, the Swiss army knife of text editors" Newsgroups: gmane.emacs.bugs Subject: bug#68006: 30.0.50; Image-mode speed Date: Thu, 17 Oct 2024 11:51:08 +0200 Message-ID: <87o73jf46r.fsf@ledu-giraud.fr> References: <87le9jlfd6.fsf@ledu-giraud.fr> <87frzjvpb5.fsf@ledu-giraud.fr> <83le9a3kqs.fsf@gnu.org> <83zfxnzyrb.fsf@gnu.org> <87mstnafie.fsf@ledu-giraud.fr> <83r0izzn1p.fsf@gnu.org> <87le95rqoh.fsf@ledu-giraud.fr> <83plyhvvso.fsf@gnu.org> <87h6jtrlci.fsf@ledu-giraud.fr> <83jzopvsgj.fsf@gnu.org> <87bka0hpto.fsf@ledu-giraud.fr> <83il48x4a8.fsf@gnu.org> <875y08gikv.fsf@ledu-giraud.fr> <831qawvx7q.fsf@gnu.org> <871qavhpxf.fsf@ledu-giraud.fr> <83edevvqyj.fsf@gnu.org> <87v887g85d.fsf@ledu-giraud.fr> <83a5pjvo3s.fsf@gnu.org> <87a5pid2zq.fsf@ledu-giraud.fr> Reply-To: Manuel Giraud Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="36759"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Cc: stefankangas@gmail.com, 68006@debbugs.gnu.org To: Eli Zaretskii Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Thu Oct 17 11:51:59 2024 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1t1NAk-0009MF-BJ for geb-bug-gnu-emacs@m.gmane-mx.org; Thu, 17 Oct 2024 11:51:58 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1t1NAV-0001xB-90; Thu, 17 Oct 2024 05:51:43 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1t1NAU-0001wO-0I for bug-gnu-emacs@gnu.org; Thu, 17 Oct 2024 05:51:42 -0400 Original-Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1t1NAT-0008HT-OB for bug-gnu-emacs@gnu.org; Thu, 17 Oct 2024 05:51:41 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debbugs.gnu.org; s=debbugs-gnu-org; h=MIME-Version:Date:References:In-Reply-To:From:To:Subject; bh=dIoDjXCXGUObhRoc+C5cxSdx8nRNBTFYvxwETacq0iI=; b=gM6ax6sXwrevXLgRGsY+I8m72kixSPpnEVi3mkVPxc4psPmFb4LIJW/pSpihKo8CI3TmqnawQwfYJv4oVvp1A2Ifn3ApG4dJW9B7H0FZEjAYX0355xhC6qU+Eh1FQDOVt433Mvyp7FWFNIYQNl5CO9EYfNGcxjgdv+htYnCKDriK2eg70SQ3K0efyG03EThjGaKQSDunV5EF4vVQpLsfjtfJOZG1tHhIANcTXG8PozAtdgWq6FNlRp0pHaQBV4EWSRYuqSqO8k9hVLHfr4LKwKX7ucSNHCqTEzSe7EF3DN5//e672FzE6e6UeGnvZszuB+0c3+VVFJi+Je+P3uGx8Q==; Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1t1NAo-0006am-EO for bug-gnu-emacs@gnu.org; Thu, 17 Oct 2024 05:52:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Manuel Giraud Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 17 Oct 2024 09:52:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 68006 X-GNU-PR-Package: emacs Original-Received: via spool by 68006-submit@debbugs.gnu.org id=B68006.172915869825299 (code B ref 68006); Thu, 17 Oct 2024 09:52:02 +0000 Original-Received: (at 68006) by debbugs.gnu.org; 17 Oct 2024 09:51:38 +0000 Original-Received: from localhost ([127.0.0.1]:33457 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1t1NAP-0006Zy-5A for submit@debbugs.gnu.org; Thu, 17 Oct 2024 05:51:38 -0400 Original-Received: from ledu-giraud.fr ([51.159.28.247]:7075) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1t1NAL-0006Zn-Sm for 68006@debbugs.gnu.org; Thu, 17 Oct 2024 05:51:35 -0400 DKIM-Signature: v=1; a=ed25519-sha256; c=simple/simple; s=ed25519; bh=BR+J09gH CT+E2YXBtyzrX9ttj/GRTO8QOvtu6pnt9wI=; h=date:references:in-reply-to: subject:cc:to:from; d=ledu-giraud.fr; b=SGGszE84g9lM39OpBbRqb7cLdOOVOe QOTmc1bRJb3czPlgpl32cEMYPjAOFdEsV7bn4gPQkIAWbznZ1gyiqSCg== DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; s=rsa; bh=BR+J09gHCT+E2YXB tyzrX9ttj/GRTO8QOvtu6pnt9wI=; h=date:references:in-reply-to:subject: cc:to:from; d=ledu-giraud.fr; b=EK2kMB7Gz529TR1pkSGk2EV0G0ISAXKtMDIXa1 v5kvPF85KEkm89X/yL4xKNuJuRyLJC5zrbO5U8fDEDYroVa+xrn4Fr4IeXLY3OcpHYTR6f ACmY2Svurfh1WacVcdj3s8pzu39Dc1jFJUWCQycFQnrmiTch+GpD4SV80SoQPkG84/HIa+ MqyvUHBucyJvBokXt3/M4a/nbLnZTDN36o+Zv+ZJJpEgfg1K5bgePYTBKrHkfHGDW2dsGI w1lUHvubNude9hsOBszH3IINX+CCnW461KjN1eH+wTrKoUAfL2wKC7vZSu+9JOPXhnJsyB tijAzNhGVg45LMQm5zBL3cNQ== Original-Received: from computer ( [10.1.1.1]) by ledu-giraud.fr (OpenSMTPD) with ESMTPSA id a0c66b2e (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO); Thu, 17 Oct 2024 11:51:09 +0200 (CEST) In-Reply-To: <87a5pid2zq.fsf@ledu-giraud.fr> (Manuel Giraud's message of "Sat, 06 Jan 2024 14:07:37 +0100") X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:293714 Archived-At: --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Hi, I'm trying to revisit this idea of having a user controlled image cache. The goal is to have, for instance, image-mode use it because (hopefully) it would have a better idea of when to cache/uncache images for its usage. Here is a patch of such a prototype =C2=AB=C2=A0user image cache=C2=A0=C2= =BB. For the moment, I wanted to keep it minimal so the only interface to use it goes as follow. When a user (or a program) use the image attribute ":ttl TIME_IN_SECONDS" with `create-image' such image will be stored in the =C2=AB=C2=A0user cache=C2=A0=C2=BB and not in the internal one. Images= lookups use both caches (internal and internal, in that order). A negative TIME_IN_SECONDS means =C2=AB=C2=A0cache this image forever=C2=A0=C2=BB. I think there will be a need for user function that completely flushes the images in this new cache. But do you think we need others commands for such interface? --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-User-controlled-image-cache.patch >From a51924c295e2141ef7b712e98d9c45adcae3bf96 Mon Sep 17 00:00:00 2001 From: Manuel Giraud Date: Thu, 17 Oct 2024 11:27:56 +0200 Subject: [PATCH] User controlled image cache --- src/dispextern.h | 4 + src/frame.h | 4 + src/image.c | 205 +++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 188 insertions(+), 25 deletions(-) diff --git a/src/dispextern.h b/src/dispextern.h index cc248a4472e..69e4d1171b3 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -3141,6 +3141,9 @@ reset_mouse_highlight (Mouse_HLInfo *hlinfo) in prepare_image_for_display. */ struct timespec timestamp; + /* The end of life for an image in the user's image cache */ + struct timespec eol; + /* Pixmaps of the image. */ Emacs_Pixmap pixmap, mask; @@ -3632,6 +3635,7 @@ #define TRY_WINDOW_IGNORE_FONTS_CHANGE (1 << 1) #ifdef HAVE_WINDOW_SYSTEM extern void clear_image_cache (struct frame *, Lisp_Object); +extern void clear_user_image_cache (struct frame *); extern ptrdiff_t image_bitmap_pixmap (struct frame *, ptrdiff_t); extern void image_reference_bitmap (struct frame *, ptrdiff_t); extern ptrdiff_t image_create_bitmap_from_data (struct frame *, char *, diff --git a/src/frame.h b/src/frame.h index 1d920d1a6bc..984568234f1 100644 --- a/src/frame.h +++ b/src/frame.h @@ -292,6 +292,9 @@ #define EMACS_FRAME_H /* Cache of realized images, which may be shared with other frames. */ struct image_cache *image_cache; + + /* Another image cache that can be managed from Emacs. */ + struct image_cache *user_image_cache; #endif /* HAVE_WINDOW_SYSTEM */ /* Tab-bar item index of the item on which a mouse button was pressed. */ @@ -916,6 +919,7 @@ #define FRAME_KBOARD(f) ((f)->terminal->kboard) /* Return a pointer to the image cache of frame F. */ #define FRAME_IMAGE_CACHE(F) ((F)->image_cache) +#define FRAME_USER_IMAGE_CACHE(F) ((F)->user_image_cache) #define XFRAME(p) \ (eassert (FRAMEP (p)), XUNTAG (p, Lisp_Vectorlike, struct frame)) diff --git a/src/image.c b/src/image.c index 34936977a40..8676ba27632 100644 --- a/src/image.c +++ b/src/image.c @@ -1744,6 +1744,7 @@ make_image (Lisp_Object spec, EMACS_UINT hash) { struct image *img = xzalloc (sizeof *img); Lisp_Object file = image_spec_value (spec, QCfile, NULL); + Lisp_Object ttl = image_spec_value (spec, QCttl, NULL); eassert (valid_image_p (spec)); img->dependencies = NILP (file) ? Qnil : list1 (file); @@ -1754,19 +1755,35 @@ make_image (Lisp_Object spec, EMACS_UINT hash) img->ascent = DEFAULT_IMAGE_ASCENT; img->hash = hash; img->corners[BOT_CORNER] = -1; /* Full image */ + if (! NILP (ttl) && FIXNUMP (ttl)) + { + struct timespec now = current_timespec (); + struct timespec ttl_spec; + + /* A negative TTL is used to mean forever. */ + if (XFIXNUM (ttl) < 0) + (img->eol).tv_sec = -1; + else + { + ttl_spec.tv_sec = XFIXNUM (ttl); + ttl_spec.tv_nsec = 0; + img->eol = timespec_add(now, ttl_spec); + } + } + else + (img->eol).tv_sec = (img->eol).tv_nsec = 0; + return img; } -/* Free image IMG which was used on frame F, including its resources. */ +/* Free image IMG from the cache C, including its resources on frame F. */ static void -free_image (struct frame *f, struct image *img) +free_image (struct image_cache *c, struct frame *f, struct image *img) { if (img) { - struct image_cache *c = FRAME_IMAGE_CACHE (f); - /* Remove IMG from the hash table of its cache. */ if (img->prev) img->prev->next = img->next; @@ -2179,6 +2196,7 @@ image_alloc_image_color (struct frame *f, struct image *img, ***********************************************************************/ static void cache_image (struct frame *f, struct image *img); +static void user_cache_image (struct frame *f, struct image *img); /* Return a new, initialized image cache that is allocated from the heap. Call free_image_cache to free an image cache. */ @@ -2197,15 +2215,14 @@ make_image_cache (void) return c; } -/* Find an image matching SPEC in the cache, and return it. If no +/* Find an image matching SPEC in the cache C, and return it. If no image is found, return NULL. */ static struct image * -search_image_cache (struct frame *f, Lisp_Object spec, EMACS_UINT hash, +search_image_cache (struct image_cache *c, Lisp_Object spec, EMACS_UINT hash, unsigned long foreground, unsigned long background, int font_size, char *font_family, bool ignore_colors) { struct image *img; - struct image_cache *c = FRAME_IMAGE_CACHE (f); int i = hash % IMAGE_CACHE_BUCKETS_SIZE; if (!c) return NULL; @@ -2258,11 +2275,13 @@ filter_image_spec (Lisp_Object spec) spec = XCDR (spec); /* Some animation-related data doesn't affect display, but - breaks the image cache. Filter those out. */ + breaks the image cache. Filter those out. Also filter out + the user's cache time to live. */ if (!(EQ (key, QCanimate_buffer) || EQ (key, QCanimate_tardiness) || EQ (key, QCanimate_position) - || EQ (key, QCanimate_multi_frame_data))) + || EQ (key, QCanimate_multi_frame_data) + || EQ (key, QCttl))) { out = Fcons (value, out); out = Fcons (key, out); @@ -2279,14 +2298,15 @@ uncache_image (struct frame *f, Lisp_Object spec) { struct image *img; EMACS_UINT hash = sxhash (filter_image_spec (spec)); + struct image_cache *c = FRAME_IMAGE_CACHE (f); /* Because the background colors are based on the current face, we can have multiple copies of an image with the same spec. We want to remove them all to ensure the user doesn't see an old version of the image when the face changes. */ - while ((img = search_image_cache (f, spec, hash, 0, 0, 0, NULL, true))) + while ((img = search_image_cache (c, spec, hash, 0, 0, 0, NULL, true))) { - free_image (f, img); + free_image (c, f, img); /* As display glyphs may still be referring to the image ID, we must garbage the frame (Bug#6426). */ SET_FRAME_GARBAGED (f); @@ -2310,7 +2330,7 @@ free_image_cache (struct frame *f) eassert (c->refcount == 0); for (i = 0; i < c->used; ++i) - free_image (f, c->images[i]); + free_image (c, f, c->images[i]); xfree (c->images); xfree (c->buckets); xfree (c); @@ -2346,7 +2366,7 @@ clear_image_cache (struct frame *f, Lisp_Object filter) if (img && (EQ (Qt, filter) || !NILP (Fmember (filter, img->dependencies)))) { - free_image (f, img); + free_image (c, f, img); ++nfreed; } } @@ -2377,7 +2397,7 @@ clear_image_cache (struct frame *f, Lisp_Object filter) struct image *img = c->images[i]; if (img && timespec_cmp (img->timestamp, old) < 0) { - free_image (f, img); + free_image (c, f, img); ++nfreed; } } @@ -2405,6 +2425,56 @@ clear_image_cache (struct frame *f, Lisp_Object filter) } } +void +clear_user_image_cache (struct frame *f) +{ + struct image_cache *c = FRAME_USER_IMAGE_CACHE (f); + + if (c && !f->inhibit_clear_image_cache) + { + ptrdiff_t i, nfreed = 0; + struct timespec t = current_timespec (); + + /* Block input so that we won't be interrupted by a SIGIO + while being in an inconsistent state. */ + block_input (); + + /* Filter image cache based on image's time to live. */ + for (i = 0; i < c->used; ++i) + { + struct image *img = c->images[i]; + if (img) + { + if (((img->eol).tv_sec >= 0) && timespec_cmp (t, img->eol) > 0) + { + free_image (c, f, img); + ++nfreed; + } + } + } + + /* We may be clearing the image cache because, for example, + Emacs was iconified for a longer period of time. In that + case, current matrices may still contain references to + images freed above. So, clear these matrices. */ + if (nfreed) + { + Lisp_Object tail, frame; + + FOR_EACH_FRAME (tail, frame) + { + struct frame *fr = XFRAME (frame); + if (FRAME_USER_IMAGE_CACHE (fr) == c) + clear_current_matrices (fr); + } + + windows_or_buffers_changed = 19; + } + + unblock_input (); + } +} + void clear_image_caches (Lisp_Object filter) { @@ -2415,7 +2485,10 @@ clear_image_caches (Lisp_Object filter) Lisp_Object tail, frame; FOR_EACH_FRAME (tail, frame) if (FRAME_WINDOW_P (XFRAME (frame))) - clear_image_cache (XFRAME (frame), filter); + { + clear_image_cache (XFRAME (frame), filter); + clear_user_image_cache (XFRAME (frame)); + } } DEFUN ("clear-image-cache", Fclear_image_cache, Sclear_image_cache, @@ -2444,7 +2517,11 @@ DEFUN ("clear-image-cache", Fclear_image_cache, Sclear_image_cache, if (! (NILP (filter) || FRAMEP (filter))) clear_image_caches (filter); else - clear_image_cache (decode_window_system_frame (filter), Qt); + { + struct frame *decoded_frame = decode_window_system_frame (filter); + clear_image_cache (decoded_frame, Qt); + clear_user_image_cache (decoded_frame); + } /* Also clear the animation caches. */ image_prune_animation_caches (true); @@ -2501,9 +2578,8 @@ image_size_in_bytes (struct image *img) } static size_t -image_frame_cache_size (struct frame *f) +image_frame_cache_size (struct image_cache *c) { - struct image_cache *c = FRAME_IMAGE_CACHE (f); if (!c) return 0; @@ -3495,6 +3571,7 @@ lookup_image (struct frame *f, Lisp_Object spec, int face_id) unsigned long background = face->background; int font_size = face->font->pixel_size; char *font_family = SSDATA (face->lface[LFACE_FAMILY_INDEX]); + struct image_cache *c; /* F must be a window-system frame, and SPEC must be a valid image specification. */ @@ -3503,20 +3580,39 @@ lookup_image (struct frame *f, Lisp_Object spec, int face_id) /* Look up SPEC in the hash table of the image cache. */ hash = sxhash (filter_image_spec (spec)); - img = search_image_cache (f, spec, hash, foreground, background, + c = FRAME_IMAGE_CACHE (f); + img = search_image_cache (c, spec, hash, foreground, background, font_size, font_family, false); if (img && img->load_failed_p) { - free_image (f, img); + free_image (c, f, img); img = NULL; } - /* If not found, create a new image and cache it. */ + /* If not found, try the user's image cache. */ + if (img == NULL) + { + c = FRAME_USER_IMAGE_CACHE (f); + img = search_image_cache (c, spec, hash, foreground, background, + font_size, font_family, false); + if (img && img->load_failed_p) + { + free_image (c, f, img); + img = NULL; + } + } + + /* If not found again, create a new image and cache it. */ if (img == NULL) { block_input (); img = make_image (spec, hash); - cache_image (f, img); + /* If image's end of life is set store it in the user's image + cache instead. */ + if ((img->eol).tv_sec != 0) + user_cache_image (f, img); + else + cache_image (f, img); img->face_foreground = foreground; img->face_background = background; img->face_font_size = font_size; @@ -3647,6 +3743,42 @@ cache_image (struct frame *f, struct image *img) c->buckets[i] = img; } +static void +user_cache_image (struct frame *f, struct image *img) +{ + struct image_cache *c = FRAME_USER_IMAGE_CACHE (f); + ptrdiff_t i; + + if (!c) + { + c = FRAME_USER_IMAGE_CACHE (f) = make_image_cache (); + c->refcount++; + } + + /* Find a free slot in c->images. */ + for (i = 0; i < c->used; ++i) + if (c->images[i] == NULL) + break; + + /* If no free slot found, maybe enlarge c->images. */ + if (i == c->used && c->used == c->size) + c->images = xpalloc (c->images, &c->size, 1, -1, sizeof *c->images); + + /* Add IMG to c->images, and assign IMG an id. */ + c->images[i] = img; + img->id = i; + if (i == c->used) + ++c->used; + + /* Add IMG to the cache's hash table. */ + i = img->hash % IMAGE_CACHE_BUCKETS_SIZE; + img->next = c->buckets[i]; + if (img->next) + img->next->prev = img; + img->prev = NULL; + c->buckets[i] = img; +} + #if defined (HAVE_WEBP) || defined (HAVE_GIF) @@ -12793,11 +12925,14 @@ DEFUN ("image-cache-size", Fimage_cache_size, Simage_cache_size, 0, 0, 0, { Lisp_Object tail, frame; size_t total = 0; + struct frame *f; FOR_EACH_FRAME (tail, frame) - if (FRAME_WINDOW_P (XFRAME (frame))) - total += image_frame_cache_size (XFRAME (frame)); - + { + f = XFRAME (frame); + if (FRAME_WINDOW_P (f)) + total += image_frame_cache_size (FRAME_IMAGE_CACHE (f)); + } #if defined (HAVE_WEBP) || defined (HAVE_GIF) struct anim_cache *pcache = anim_cache; while (pcache) @@ -12810,6 +12945,24 @@ DEFUN ("image-cache-size", Fimage_cache_size, Simage_cache_size, 0, 0, 0, return make_int (total); } +DEFUN ("user-image-cache-size", Fuser_image_cache_size, Suser_image_cache_size, 0, 0, 0, + doc: /* Return the size of the user's image cache. */) + (void) +{ + Lisp_Object tail, frame; + size_t total = 0; + struct frame *f; + + FOR_EACH_FRAME (tail, frame) + { + f = XFRAME (frame); + if (FRAME_WINDOW_P (f)) + total += image_frame_cache_size (FRAME_USER_IMAGE_CACHE (f)); + } + + return make_int (total); +} + DEFUN ("init-image-library", Finit_image_library, Sinit_image_library, 1, 1, 0, doc: /* Initialize image library implementing image type TYPE. @@ -12979,6 +13132,7 @@ syms_of_image (void) DEFSYM (QCcolor_adjustment, ":color-adjustment"); DEFSYM (QCmask, ":mask"); DEFSYM (QCflip, ":flip"); + DEFSYM (QCttl, ":ttl"); /* Other symbols. */ DEFSYM (Qlaplace, "laplace"); @@ -13142,6 +13296,7 @@ syms_of_image (void) defsubr (&Simage_mask_p); defsubr (&Simage_metadata); defsubr (&Simage_cache_size); + defsubr (&Suser_image_cache_size); defsubr (&Simagep); #ifdef GLYPH_DEBUG -- 2.46.2 --=-=-= Content-Type: text/plain -- Manuel Giraud --=-=-=--