unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Need `truncate-string-to-pixel-width` and `glyph-pixel-width` functions in C
@ 2024-10-23  5:01 Jimmy Yuen Ho Wong
  2024-10-23  8:01 ` Eli Zaretskii
  0 siblings, 1 reply; 6+ messages in thread
From: Jimmy Yuen Ho Wong @ 2024-10-23  5:01 UTC (permalink / raw)
  To: Emacs-Devel devel

[-- Attachment #1: Type: text/plain, Size: 1815 bytes --]

Dear emacs-devel,

Recently I've been trying to mold Corfu
<https://github.com/minad/corfu/pull/508> to render every string in every
face in pixel precision but I've run into some slight performance issues
when truncating strings. The motivation for this is
`truncate-string-to-width` not only does not speak pixels, but also doesn't
understand composed characters such as emojis. It just chops off the emoji
byte sequence in the middle as described in the PR, so I had to write a
custom `truncate-string-to-pixel-width` function using `string-glyph-split`
and `string-pixel-width` to measure character widths, which seems... silly.

I've looked into how to get the width of a glyph from a font object by
employing some dark magic of undocumented internal functions and wrongly
documented functions such as `char-displayable-p`, `font-info`,
`lglyth-width`, `lgstring-glyph`, `open-font` etc in combination of
`composition-get-gstring`, which is correct and fast enough.... for glyph
strings with no faces. Then I looked into reverse engineering the face font
selection process, which quickly pushed the run time up 2 orders of
magnitude due to the anonymous face + face attribute inheritance + face
lists madness. Then when I realized I still didn't deal with display text
properties, I threw my hands up.

It'd be really nice if we could have a really performant C function that
can truncate strings correctly and preferably in pixel precision.
Alternatively, a `glyph-pixel-width` that implements a fast path in C
that's somewhat akin to the process I described above, but also handles
display text properties correctly would be nice.

I don't really know enough about Emacs C internals WRT to font rendering to
implement this so I'm just throwing this idea out there.

Does this make sense?

Jimmy Yuen Ho Wong

[-- Attachment #2: Type: text/html, Size: 2224 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Need `truncate-string-to-pixel-width` and `glyph-pixel-width` functions in C
  2024-10-23  5:01 Need `truncate-string-to-pixel-width` and `glyph-pixel-width` functions in C Jimmy Yuen Ho Wong
@ 2024-10-23  8:01 ` Eli Zaretskii
  2024-10-23 13:52   ` Jimmy Yuen Ho Wong
  0 siblings, 1 reply; 6+ messages in thread
From: Eli Zaretskii @ 2024-10-23  8:01 UTC (permalink / raw)
  To: Jimmy Yuen Ho Wong; +Cc: emacs-devel

> From: Jimmy Yuen Ho Wong <wyuenho@gmail.com>
> Date: Wed, 23 Oct 2024 06:01:18 +0100
> 
> Recently I've been trying to mold Corfu to render every string in every face in pixel precision but I've run into
> some slight performance issues when truncating strings. The motivation for this is `truncate-string-to-width`
> not only does not speak pixels, but also doesn't understand composed characters such as emojis. It just
> chops off the emoji byte sequence in the middle as described in the PR, so I had to write a custom
> `truncate-string-to-pixel-width` function using `string-glyph-split` and `string-pixel-width` to measure character
> widths, which seems... silly.
> 
> I've looked into how to get the width of a glyph from a font object by employing some dark magic of
> undocumented internal functions and wrongly documented functions such as `char-displayable-p`, `font-info`,
> `lglyth-width`, `lgstring-glyph`, `open-font` etc in combination of `composition-get-gstring`, which is correct and
> fast enough.... for glyph strings with no faces. Then I looked into reverse engineering the face font selection
> process, which quickly pushed the run time up 2 orders of magnitude due to the anonymous face + face
> attribute inheritance + face lists madness. Then when I realized I still didn't deal with display text properties, I
> threw my hands up.
> 
> It'd be really nice if we could have a really performant C function that can truncate strings correctly and
> preferably in pixel precision. Alternatively, a `glyph-pixel-width` that implements a fast path in C that's
> somewhat akin to the process I described above, but also handles display text properties correctly would be
> nice.
> 
> I don't really know enough about Emacs C internals WRT to font rendering to implement this so I'm just
> throwing this idea out there.

I know next to nothing about Corfu, so I have hard time understanding
what you are trying to do and why.  Basically, the Emacs display
engine already truncates stuff it displays, so it is completely
unclear to me why you'd need to do that in Lisp.

In particular, "truncate strings in pixel precision" makes no sense to
me, since strings on display are made of characters (more accurately,
"grapheme clusters"), so any reasonable truncation will always work at
the grapheme-cluster granularity, not at pixel granularity.

So please describe your problem in much more detail, assuming that I
know nothing about Corfu and its display-related tricks.

Thanks.



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Need `truncate-string-to-pixel-width` and `glyph-pixel-width` functions in C
  2024-10-23  8:01 ` Eli Zaretskii
@ 2024-10-23 13:52   ` Jimmy Yuen Ho Wong
  2024-10-23 17:39     ` Eli Zaretskii
  0 siblings, 1 reply; 6+ messages in thread
From: Jimmy Yuen Ho Wong @ 2024-10-23 13:52 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1689 bytes --]

On Wed, Oct 23, 2024 at 9:01 AM Eli Zaretskii <eliz@gnu.org> wrote:

> I know next to nothing about Corfu, so I have hard time understanding
> what you are trying to do and why.  Basically, the Emacs display
> engine already truncates stuff it displays, so it is completely
> unclear to me why you'd need to do that in Lisp.
>

Corfu is like Company, it's a completion frontend that formats a list of
completion candidates to fit inside a confined pop up box.


> In particular, "truncate strings in pixel precision" makes no sense to
> me, since strings on display are made of characters (more accurately,
> "grapheme clusters"), so any reasonable truncation will always work at
> the grapheme-cluster granularity, not at pixel granularity.
>

As described, `truncate-string-to-width` doesn't even truncate at grapheme
cluster granularity correctly.

A `truncate-string-to-pixel-width` function is needed because propertized
strings may consist of display space text properties, images, emojis in a
different font, and faces in variable fonts, which all affect glyph widths,
you can't simply assume every glyph will take up 1 column. As a result,
when you calculate some `end-column` and whether it has reached the width
constraint in pixels, one would necessarily need to know the exact width in
pixels for each glyph. Converting the width constraint in pixels to columns
in order to supply it as the second parameter to a correctly implemented
`truncate-string-to-width` amounts to the exact same procedure. Therefore,
either a `truncate-string-to-pixel-width` or `glyph-pixel-width` function
is needed, because `string-pixel-width` is very expensive.

[-- Attachment #2: Type: text/html, Size: 2282 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Need `truncate-string-to-pixel-width` and `glyph-pixel-width` functions in C
  2024-10-23 13:52   ` Jimmy Yuen Ho Wong
@ 2024-10-23 17:39     ` Eli Zaretskii
  2024-10-23 20:03       ` Jimmy Yuen Ho Wong
  0 siblings, 1 reply; 6+ messages in thread
From: Eli Zaretskii @ 2024-10-23 17:39 UTC (permalink / raw)
  To: Jimmy Yuen Ho Wong; +Cc: emacs-devel

> From: Jimmy Yuen Ho Wong <wyuenho@gmail.com>
> Date: Wed, 23 Oct 2024 14:52:53 +0100
> Cc: emacs-devel@gnu.org
> 
> On Wed, Oct 23, 2024 at 9:01 AM Eli Zaretskii <eliz@gnu.org> wrote:
> 
>  I know next to nothing about Corfu, so I have hard time understanding
>  what you are trying to do and why.  Basically, the Emacs display
>  engine already truncates stuff it displays, so it is completely
>  unclear to me why you'd need to do that in Lisp.
> 
> Corfu is like Company, it's a completion frontend that formats a list of completion candidates to fit inside a
> confined pop up box.

Thanks, but that doesn't explain enough.  Please tell more about the
need to truncate completion candidates "inside a confined pop up box".
If that box is displayed, why doesn't Emacs truncate the text
automatically?  Or maybe I don't understand how this "pop up box",
whatever it is, is displayed?

> A `truncate-string-to-pixel-width` function is needed because propertized strings may consist of display space
> text properties, images, emojis in a different font, and faces in variable fonts, which all affect glyph widths, you
> can't simply assume every glyph will take up 1 column. As a result, when you calculate some `end-column`
> and whether it has reached the width constraint in pixels, one would necessarily need to know the exact width
> in pixels for each glyph. Converting the width constraint in pixels to columns in order to supply it as the second
> parameter to a correctly implemented `truncate-string-to-width` amounts to the exact same procedure.
> Therefore, either a `truncate-string-to-pixel-width` or `glyph-pixel-width` function is needed, because
> `string-pixel-width` is very expensive.

So the issue is not "pixel-wise truncation", the issue is to take
variable-width display elements into account when truncating, is that
right?



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Need `truncate-string-to-pixel-width` and `glyph-pixel-width` functions in C
  2024-10-23 17:39     ` Eli Zaretskii
@ 2024-10-23 20:03       ` Jimmy Yuen Ho Wong
  2024-10-24  9:55         ` Eli Zaretskii
  0 siblings, 1 reply; 6+ messages in thread
From: Jimmy Yuen Ho Wong @ 2024-10-23 20:03 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1653 bytes --]

>
> Thanks, but that doesn't explain enough.  Please tell more about the
> need to truncate completion candidates "inside a confined pop up box".
> If that box is displayed, why doesn't Emacs truncate the text
> automatically?  Or maybe I don't understand how this "pop up box",
> whatever it is, is displayed?


Each completion in a list of completion candidates is a triple of
(candidate string, prefix, annotation), all in varying widths. Aligning
these 3 columns requires calculating paddings. Since there are paddings,
the padding between the candidate string and the annotation can also be
arbitrarily large. Naive truncation that truncates the entire concatenated
line against the width of the pop up box, which is actually the window
width in a child frame, may leave some arbitrarily long empty space in the
end. In addition, naive truncation does not replace extra characters in the
relevant columns with ellipses to indicate truncation happened. Please look
at the link in my first email for further details.


> So the issue is not "pixel-wise truncation", the issue is to take
> variable-width display elements into account when truncating, is that
> right?
>

To be precise, by " truncate strings correctly and preferably in pixel
precision", I mean truncation that respects pixel width constraints (and
grapheme clusters, as clarified by you). Truncating variable-width display
elements on GUIs necessitate the ability to calculate the widths of the
individual elements, I call this the hypothetical `glyph-pixel-width`.
Whether it is exposed to Elisp is less important to a hypothetical
`truncate-string-to-pixel-width` for my use case.

[-- Attachment #2: Type: text/html, Size: 2107 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Need `truncate-string-to-pixel-width` and `glyph-pixel-width` functions in C
  2024-10-23 20:03       ` Jimmy Yuen Ho Wong
@ 2024-10-24  9:55         ` Eli Zaretskii
  0 siblings, 0 replies; 6+ messages in thread
From: Eli Zaretskii @ 2024-10-24  9:55 UTC (permalink / raw)
  To: Jimmy Yuen Ho Wong; +Cc: emacs-devel

> From: Jimmy Yuen Ho Wong <wyuenho@gmail.com>
> Date: Wed, 23 Oct 2024 21:03:55 +0100
> Cc: emacs-devel@gnu.org
> 
> Each completion in a list of completion candidates is a triple of (candidate string, prefix, annotation), all in
> varying widths. Aligning these 3 columns requires calculating paddings. Since there are paddings, the
> padding between the candidate string and the annotation can also be arbitrarily large. Naive truncation that
> truncates the entire concatenated line against the width of the pop up box, which is actually the window width
> in a child frame, may leave some arbitrarily long empty space in the end. In addition, naive truncation does
> not replace extra characters in the relevant columns with ellipses to indicate truncation happened. Please look
> at the link in my first email for further details.
>  
>  So the issue is not "pixel-wise truncation", the issue is to take
>  variable-width display elements into account when truncating, is that
>  right?
> 
> To be precise, by " truncate strings correctly and preferably in pixel precision", I mean truncation that respects
> pixel width constraints (and grapheme clusters, as clarified by you). Truncating variable-width display
> elements on GUIs necessitate the ability to calculate the widths of the individual elements, I call this the
> hypothetical `glyph-pixel-width`. Whether it is exposed to Elisp is less important to a hypothetical
> `truncate-string-to-pixel-width` for my use case.

OK, thanks.

So let me see if I understand the issue.  You want to create in a
child frame a display such as below:

  <icon1><candidate1>                      <Type1>
  <icon2><longer-name-candidate>     <AnotherType>
  ...

And you want to do it in a way that all the "Type"s are aligned to the
right (i.e. end at the right edge of the frame), no matter what is
shown in the <icon>s and in the <candidate>s, which could be of
different width and could include characters of different fonts and
even images/emoji.  Is that correct, or is there something else?

If the above is what you want, and since the popup is actually a
frame, my first suggestion would be to use :align-to display property
on the whitespace between the <candidate>s and the <Type>s.  Did you
try that?

The advantage of :align-to is that you don't have to measure the
pixel-width of the candidate names, only the pixel-width of each
<Type> string (to correctly set up the offset from the right edge of
the text area) -- the rest will be done by the display engine.  And
the display engine always does everything in pixel resolution.



^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-10-24  9:55 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-23  5:01 Need `truncate-string-to-pixel-width` and `glyph-pixel-width` functions in C Jimmy Yuen Ho Wong
2024-10-23  8:01 ` Eli Zaretskii
2024-10-23 13:52   ` Jimmy Yuen Ho Wong
2024-10-23 17:39     ` Eli Zaretskii
2024-10-23 20:03       ` Jimmy Yuen Ho Wong
2024-10-24  9:55         ` Eli Zaretskii

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).