kitty
kitty copied to clipboard
Add support for subpixel rendering on Linux
Adds optional support for subpixel rendering when using freetype. This behavior is disabled by default and can be enabled in the configuration file.
Grayscale:
Subpixel:
Very impressive for your first contribution to kitty!
I think it would be better to rename linux_use_subpixel_rendering
to use_subpixel_rendering
or maybe simply subpixel_rendering
, so that if we add support for macOS in the future, we don't have to rename the option or have two options that do the same thing just on two different platforms. You could add a sentence to the description of the option, mentioning that it doesn't work on macOS at the moment. I just spent the past couple hours installing a FreeBSD VM and installing kitty and it also works there 🎉, so the linux
part of the option name wouldn't be entirely accurate anyways.
What do you think @kovidgoyal?
How do you ensure that kitty uses the correct color on the correct side? In my FreeBSD VM the left side of the characters in kitty is blue while the right side is red. In other programs in my VM and outside of my VM the left side is red while the right side is blue. Doing sub-pixel anti-aliasing wrong in this way will make the font look worse than using no sub-pixel anti-aliasing. Which side needs to have which color might depend on the monitor though, idk.
Yes, I think it should be use_subpixel_rendering. As for BGR vs RGB IIRC that comes from the fontconfig configuration on Linux.
@Luflosi when rendering using FT_RENDER_MODE_LCD
FreeType produces either RGB or BGR depending on the fontconfig configuration. I haven't tested it thoroughly thou. Also, it might be worth adding support for FT_RENDER_MODE_LCD_V
which is used on vertical screens. And I'll definitely remove linux_
prefix.
I started working on adding support for LCD_V
. It requires a bit of work, as the resulted bitmap is thrice the height of the actual image, so rows
is no longer reliable to get the height of a ProcessedBitmap
instance.
I investigated some more and it turns out that kitty correctly uses RGB in my FreeBSD VM. I made a mistake when thinking about which colors should be on which side, sorry for that.
@nightuser I would not bother with LCD_V if I were you. Its a lot of work and is very rare in actual hardware. I think the only time it is used is if the monitor is rotated.
@kovidgoyal well, I find out that some additional work needed for regular LCD, as there are some places in the code where we use that bitmap is grayscale. Also, I have a lot of acquaintances who use their monitors in the vertical orientation, myself included.
If it is important to you feel free to implement support for vertical, I have no objections.
Hi!
Sorry, I was a bit busy with my life.
By the way, I implemented the support for vertical alignment and fixed some places, where it was implicitly assumed that the rendering is grayscale. I tested it with four possible combinations (RGB/BGR and horizontal/vertical), and it renders correctly.
No worries, it is going to be some time before I can find the time to review this.
What would it take to add support for macOS?
I gave this patch a try, but it doesn't seem to work correctly with transparency yet. Around each character, there is a solid rectangle. (On Xorg with compton, using Intel graphics.)
Really looking forward to this patch though, since most of my displays are low DPI.
What would it take to add support for macOS?
There would need ot be some way to get CoreText to render multi-channel bitmaps. Since Apple has officially decided to remove sub-pixel rendering, this seems unlikely to be possible.
I didn't use transparent background, so I forgot to test it. From what I see, it should be something easy to fix. I'll look into it later.
On Fri, May 24, 2019, 14:36 miseran [email protected] wrote:
I gave this patch a try, but it doesn't seem to work correctly with transparency yet. Around each character, there is a solid rectangle. (On Xorg with compton, using Intel graphics.)
[image: transparency] https://user-images.githubusercontent.com/6820152/58324086-1eacaa00-7e26-11e9-840f-c01f769c04f5.png
Really looking forward to this patch though, since most of my displays are low DPI.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/kovidgoyal/kitty/pull/1604?email_source=notifications&email_token=AAN6F6D3K2ZBGHHRDMCRSLLPW7HKVA5CNFSM4HL5WQL2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODWFAW5I#issuecomment-495586165, or mute the thread https://github.com/notifications/unsubscribe-auth/AAN6F6CIEOPI6PRCIQRYJCTPW7HKVANCNFSM4HL5WQLQ .
@kovidgoyal but is it possible to use Freetype on macOS (an optional switch maybe).
People on macs complain a lot if text rendering is different from Apple's which is why I took the trouble of implementing it using CoreText in the first place. I'm not really in favor of fighting the platform's official stance. If Apple does not want to support sub-pixel anti-aliasing, then that is their choice. Without official support it means there is no global setting for the pixel stripes order RGB vs BGR or horizontal vs vertical. And then FreeType will behave in subtly different ways than CoreText leading to more hard to replicate bugs that I will have to debug.
Turns out, it's nearly impossible to implement transparency accurately, at least without rewriting much of the rendering (see https://stackoverflow.com/questions/33507617/blending-text-rendered-by-freetype-in-color-and-alpha , for example).
@kovidgoyal could you help me with this short block of code: https://github.com/nightuser/kitty/blob/freetype_subpixel_rendering/kitty/cell_fragment.glsl#L64 (lines 64-69), please? I'm not fluent in OpenGL at all, so I am probably doing something wrong there.
@miseran I added experimental support for transparency. Could you test it out?
I rebased my branch against current master because I personally use this patch as a daily driver.
On Sun, May 26, 2019 at 12:06:49PM -0700, nightuser wrote:
@kovidgoyal could you help me with this short block of code: https://github.com/nightuser/kitty/blob/freetype_subpixel_rendering/kitty/cell_fragment.glsl#L71 , please? I'm not fluent in OpenGL at all, so I am probably doing something wrong there.
Sure, I am happy to help, what is your question? I dont know exactly what you are trying to do there, so hard for me to say if you are right or wrong.
@kovidgoyal Freetype provides alpha channel per color for each pixel separately. I'm blending the foreground and the background using mix
: for a non-transparent background it's as simple as mix(background, foreground, text_fg.rgb)
. In the case of a transparent background, things get a little hairy:
- I use
background
input variable, but I'm not sure whether multiplying bybg_alpha
was already done (it seems that it isn't, but I don't really understand why we need to multiply twice). - We should render only the actual character and not the bg around it. Unfortunately, Freetype gives a mask with "transparent" area which usually should be filled with bg color. Cairo library faces a similar problem and they're using the green channel as the alpha channel. So, I've come up with the following lines:
It somehow works, but I'm worried about the fact thatfloat alpha = text_fg.g; vec3 scaled_mask = mix(vec3(1.0), text_fg.rgb / alpha, bvec3(alpha > 0)); vec3 blended_fg = mix(background * bg_alpha * bg_alpha, foreground, scaled_mask);
scaled_mask
here might become non-normalized (i.e. we can get values bigger than 1). Also, the wayscaled_mask
is constructed looks kinda ugly.
See the comment at the start of the main() function under #ifdef TRANSPARENT for why bg_alpha is doubled.
As for scaled_mask, I will look at it a littl elater, when I have some peace and quiet.
Regarding scaled mask. I dont quite understand the problem. The only difference between the transparent and opaque bg cases is that the background has its own alpha, therefore you cannot simply mix foreground and background colors. Instead you have to do a full per-channle alpha blend. See the alpha_blend() function for an example. That function has a single alpha for the under and a single alpha for the over color. The difference in your case is you have three alphas for the over color. It should be possible to write a similar function to do full per-channel alpha blending. This shuld take care of the problem with freetype transparent pixels, since they get rendered in the background color automatically, by virtue of the alpha blend.
@kovidgoyal the problem arises because at the end we combine bg and fg textures one above the other. So, if these transparent pixels are rendered in bg color (even if it's semi-transparent), we will draw two semi-transparent backgrounds in some places, which results in more-opaque color.
In opaque case, it's okay, as an opaque image over opaque looks fine.
@kovidgoyal It seems that it can probably be done with correct glBlendFunc
call. I'll look into it later.
@kovidgoyal right now I don't understand how glBlendFunc(...)
works, it doesn't have any effect in draw_cells_simple
. Or is not used there for blending foreground and background?
@nightuser I will re-write that shader to make it clear exactly what is happening in the various rendering scenarios (simple, split, transparent, etc) which should make it possible for you to implement proper alpha blending.
@nightuser as promised I have simplified and documented the cell fragment shader. And also gotten rid of the double bg_alpha multiplication as it was not really needed. Let me know if you have any further questions.