Repository: kovidgoyal/kitty

Issue:
Dynamic font scaling (with font size change keys) isn't perfect
![ezgif-1-e542106769cf](https://user-images.githubusercontent.com/9730330/65805335-54bbbb00-e185-11e9-99c2-99e4e7d12d22.gif)
The text is:
```
████████
██████
████████
```
(rendering in the browser is pretty broken ^^)
The offending char symbol is (hex unicode): `e0be` with the font: `DejaVuSansMono Nerd Font Mono`.

Do you have any idea why the font scaling is doing that? Can it be avoided?




Discussion:
It has nothing to do with font scaling but rather rendering of that particular symbol. For these kinds of symbols, you cant really use font rendering because they should exactly fill the cell. Instead they should be drawn manually, you can add drawing overrides for them in box_drawing.py where IIRC there are already a few PUA symbols used by nerdfonts/powerline overridden.
It looks like this symbol is not managed by the box_drawing primitives, thus it falls back to a font and fails to scale properly as you said..

I'm trying to implement it in box_drawing, but I don't really understand how the function `fill_buffer` is supposed to be used, especially the `xlimits` parameter.

Could you help me on that? Thank you
It is simply the limits within which to antialias. But you dont have to use this function if you dont want to. Rendering simply involves filling the cell buffer with numbers between 0 and 255 indicating how much opacity to apply at that pixel

Resolving patch:
diff --git a/kittens/icat/main.py b/kittens/icat/main.py
--- a/kittens/icat/main.py
+++ b/kittens/icat/main.py
@@ -22,7 +22,6 @@
 )
 from ..tui.operations import clear_images_on_screen, serialize_gr_command
 
-screen_size = None
 OPTIONS = '''\
 --align
 type=choices
@@ -97,6 +96,16 @@
 '''
 
 
+screen_size = None
+
+
+def get_screen_size():
+    global screen_size
+    if screen_size is None:
+        screen_size = screen_size_function()
+    return screen_size()
+
+
 def options_spec():
     if not hasattr(options_spec, 'ans'):
         options_spec.ans = OPTIONS.format(
@@ -122,7 +131,7 @@ def calculate_in_cell_x_offset(width, cell_width, align):
 
 
 def set_cursor(cmd, width, height, align):
-    ss = screen_size()
+    ss = get_screen_size()
     cw = int(ss.width / ss.cols)
     num_of_cells_needed = int(ceil(width / cw))
     if num_of_cells_needed > ss.cols:
@@ -143,7 +152,7 @@ def set_cursor(cmd, width, height, align):
 
 def set_cursor_for_place(place, cmd, width, height, align):
     x = place.left + 1
-    ss = screen_size()
+    ss = get_screen_size()
     cw = int(ss.width / ss.cols)
     num_of_cells_needed = int(ceil(width / cw))
     cmd['X'] = calculate_in_cell_x_offset(width, cw, align)
@@ -189,7 +198,7 @@ def show(outfile, width, height, fmt, transmit_mode='t', align='center', place=N
 
 def process(path, args, is_tempfile):
     m = identify(path)
-    ss = screen_size()
+    ss = get_screen_size()
     available_width = args.place.width * (ss.width / ss.cols) if args.place else ss.width
     available_height = args.place.height * (ss.height / ss.rows) if args.place else 10 * m.height
     needs_scaling = m.width > available_width or m.height > available_height
@@ -334,7 +343,7 @@ def main(args=sys.argv):
         sys.stdin.close()
         sys.stdin = open(os.ctermid(), 'r')
 
-    screen_size = screen_size_function()
+    screen_size = get_screen_size()
     signal.signal(signal.SIGWINCH, lambda signum, frame: setattr(screen_size, 'changed', True))
     if screen_size().width == 0:
         if args.detect_support:
diff --git a/kitty/fonts/box_drawing.py b/kitty/fonts/box_drawing.py
--- a/kitty/fonts/box_drawing.py
+++ b/kitty/fonts/box_drawing.py
@@ -2,6 +2,11 @@
 # vim:fileencoding=utf-8
 # License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
 
+#
+# NOTE: to add a new glyph, add an entry to the `box_chars` dict, then update
+# the functions `font_for_cell` and `box_glyph_id` in `kitty/fonts.c`.
+#
+
 import math
 from functools import partial as p
 from itertools import repeat
@@ -172,6 +177,22 @@ def triangle(buf, width, height, left=True):
     fill_region(buf, width, height, xlimits)
 
 
+def corner_triangle(buf, width, height, corner):
+    if corner == 'top-right' or corner == 'bottom-left':
+        diagonal_y = line_equation(0, 0, width - 1, height - 1)
+        if corner == 'top-right':
+            xlimits = [(0, diagonal_y(x)) for x in range(width)]
+        elif corner == 'bottom-left':
+            xlimits = [(diagonal_y(x), height - 1) for x in range(width)]
+    else:
+        diagonal_y = line_equation(width - 1, 0, 0, height - 1)
+        if corner == 'top-left':
+            xlimits = [(0, diagonal_y(x)) for x in range(width)]
+        elif corner == 'bottom-right':
+            xlimits = [(diagonal_y(x), height - 1) for x in range(width)]
+    fill_region(buf, width, height, xlimits)
+
+
 def antialiased_1px_line(buf, width, height, p1, p2):
     # Draw an antialiased line using the Wu algorithm
     x1, y1 = p1
@@ -552,6 +573,10 @@ def quad(buf, width, height, x=0, y=0):
     '': [p(D, left=False)],
     '': [p(half_cross_line, which='tl'), p(half_cross_line, which='bl')],
     '': [p(half_cross_line, which='tr'), p(half_cross_line, which='br')],
+    '': [p(corner_triangle, corner='bottom-left')],
+    '': [p(corner_triangle, corner='bottom-right')],
+    '': [p(corner_triangle, corner='top-left')],
+    '': [p(corner_triangle, corner='top-right')],
     '═': [dhline],
     '║': [dvline],
     '╞': [vline, p(half_dhline, which='right')],
@@ -647,60 +672,60 @@ def render_missing_glyph(buf, width, height):
 
 
 def test_char(ch, sz=48):
-    # kitty +runpy "from kitty.fonts.box_drawing import test_char; import sys; test_char('XXX')"
+    # kitty +runpy "from kitty.fonts.box_drawing import test_char; test_char('XXX')"
     from .render import display_bitmap, setup_for_testing
     from kitty.fast_data_types import concat_cells, set_send_sprite_to_gpu
-    width, height = setup_for_testing('monospace', sz)[1:]
-    buf = bytearray(width * height)
-    try:
-        render_box_char(ch, buf, width, height)
+    with setup_for_testing('monospace', sz) as (_, width, height):
+        buf = bytearray(width * height)
+        try:
+            render_box_char(ch, buf, width, height)
 
-        def join_cells(*cells):
-            cells = tuple(bytes(x) for x in cells)
-            return concat_cells(width, height, False, cells)
+            def join_cells(*cells):
+                cells = tuple(bytes(x) for x in cells)
+                return concat_cells(width, height, False, cells)
 
-        rgb_data = join_cells(buf)
-        display_bitmap(rgb_data, width, height)
-        print()
-    finally:
-        set_send_sprite_to_gpu(None)
+            rgb_data = join_cells(buf)
+            display_bitmap(rgb_data, width, height)
+            print()
+        finally:
+            set_send_sprite_to_gpu(None)
 
 
 def test_drawing(sz=48, family='monospace'):
     from .render import display_bitmap, setup_for_testing
     from kitty.fast_data_types import concat_cells, set_send_sprite_to_gpu
 
-    width, height = setup_for_testing(family, sz)[1:]
-    space = bytearray(width * height)
-
-    def join_cells(cells):
-        cells = tuple(bytes(x) for x in cells)
-        return concat_cells(width, height, False, cells)
-
-    def render_chr(ch):
-        if ch in box_chars:
-            cell = bytearray(len(space))
-            render_box_char(ch, cell, width, height)
-            return cell
-        return space
-
-    pos = 0x2500
-    rows = []
-    space_row = join_cells(repeat(space, 32))
-
-    try:
-        for r in range(10):
-            row = []
-            for i in range(16):
-                row.append(render_chr(chr(pos)))
-                row.append(space)
-                pos += 1
-            rows.append(join_cells(row))
-            rows.append(space_row)
-        rgb_data = b''.join(rows)
-        width *= 32
-        height *= len(rows)
-        assert len(rgb_data) == width * height * 4, '{} != {}'.format(len(rgb_data), width * height * 4)
-        display_bitmap(rgb_data, width, height)
-    finally:
-        set_send_sprite_to_gpu(None)
+    with setup_for_testing(family, sz) as (_, width, height):
+        space = bytearray(width * height)
+
+        def join_cells(cells):
+            cells = tuple(bytes(x) for x in cells)
+            return concat_cells(width, height, False, cells)
+
+        def render_chr(ch):
+            if ch in box_chars:
+                cell = bytearray(len(space))
+                render_box_char(ch, cell, width, height)
+                return cell
+            return space
+
+        pos = 0x2500
+        rows = []
+        space_row = join_cells(repeat(space, 32))
+
+        try:
+            for r in range(10):
+                row = []
+                for i in range(16):
+                    row.append(render_chr(chr(pos)))
+                    row.append(space)
+                    pos += 1
+                rows.append(join_cells(row))
+                rows.append(space_row)
+            rgb_data = b''.join(rows)
+            width *= 32
+            height *= len(rows)
+            assert len(rgb_data) == width * height * 4, '{} != {}'.format(len(rgb_data), width * height * 4)
+            display_bitmap(rgb_data, width, height)
+        finally:
+            set_send_sprite_to_gpu(None)


Test patch: