From 37ac5d41949dd35b973cd5eb0fa7ff77b2422309 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 20 Oct 2016 12:00:14 +0530 Subject: [PATCH] Use a rendered text cache for faster painting --- kitty/config.py | 6 +++--- kitty/data_types.py | 8 +++----- kitty/term.py | 42 +++++++++++++++++++++++++++++------------- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/kitty/config.py b/kitty/config.py index 765e0b49b..1847413e7 100644 --- a/kitty/config.py +++ b/kitty/config.py @@ -120,13 +120,13 @@ def validate_font(opts: Options): def build_ansi_color_tables(opts: Options) -> Tuple[dict, dict]: def col(i): - return QColor(getattr(opts, 'color{}'.format(i))) + return QColor(getattr(opts, 'color{}'.format(i))).getRgb()[:3] fg = {30 + i: col(i) for i in range(8)} fg[39] = opts.foreground fg.update({90 + i: col(i + 8) for i in range(8)}) - fg[99] = opts.foreground_bold + fg[99] = opts.foreground_bold.getRgb()[:3] bg = {40 + i: col(i) for i in range(8)} - bg[49] = opts.background + bg[49] = opts.background.getRgb()[:3] bg.update({100 + i: col(i + 8) for i in range(8)}) build_ansi_color_tables.fg, build_ansi_color_tables.bg = fg, bg build_ansi_color_tables(defaults) diff --git a/kitty/data_types.py b/kitty/data_types.py index 20be55cd2..b1a1ee22d 100644 --- a/kitty/data_types.py +++ b/kitty/data_types.py @@ -6,8 +6,6 @@ import array from typing import Tuple, Dict, Union, Iterator, Sequence from itertools import repeat -from PyQt5.QtGui import QColor - from pyte.graphics import FG_BG_256 from .config import fg_color_table, bg_color_table @@ -305,19 +303,19 @@ class Line: self.char[i] = (a << ATTRS_SHIFT) | (c & CHAR_MASK) -def as_color(entry: int, color_table: Dict[int, QColor]) -> Union[QColor, None]: +def as_color(entry: int, color_table: Dict[int, Tuple[int]]) -> Union[Tuple[int], None]: t = entry & 0xff if t == 1: r = (entry >> 8) & 0xff return color_table.get(r) if t == 2: r = (entry >> 8) & 0xff - return QColor(*FG_BG_256[r]) + return FG_BG_256[r] if t == 3: r = (entry >> 8) & 0xff g = (entry >> 16) & 0xff b = (entry >> 24) & 0xff - return QColor(r, g, b) + return r, g, b def copy_char(src: Line, dest: Line, src_pos: int, dest_pos: int) -> None: diff --git a/kitty/term.py b/kitty/term.py index 160e94d2c..1f8eda233 100644 --- a/kitty/term.py +++ b/kitty/term.py @@ -2,10 +2,11 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2016, Kovid Goyal +from functools import lru_cache from typing import Tuple, Iterator, Sequence from PyQt5.QtCore import pyqtSignal, QTimer, QRect, Qt -from PyQt5.QtGui import QColor, QPainter, QFont, QFontMetrics, QRegion, QPen +from PyQt5.QtGui import QColor, QPainter, QFont, QFontMetrics, QRegion, QPen, QPixmap from PyQt5.QtWidgets import QWidget from .config import build_ansi_color_tables, Options, fg_color_table, bg_color_table @@ -23,6 +24,20 @@ def ascii_width(fm: QFontMetrics) -> int: return ans +@lru_cache(maxsize=2**11) +def pixmap_for_text(text, color, default_fg, font, w, h, baseline): + p = QPixmap(w, h) + p.fill(Qt.transparent) + fg = as_color(color & COL_MASK, fg_color_table()) or default_fg + painter = QPainter(p) + painter.setFont(font) + painter.setPen(QPen(QColor(*fg))) + painter.setRenderHints(QPainter.TextAntialiasing | QPainter.Antialiasing) + painter.drawText(0, baseline, text) + painter.end() + return p + + class TerminalWidget(QWidget): relayout_lines = pyqtSignal(object, object, object, object) @@ -46,14 +61,15 @@ class TerminalWidget(QWidget): def apply_opts(self, opts): self.opts = opts + pixmap_for_text.cache_clear() pal = self.palette() pal.setColor(pal.Window, QColor(opts.background)) pal.setColor(pal.WindowText, QColor(opts.foreground)) self.setPalette(pal) self.current_bg = pal.color(pal.Window) - self.current_fg = pal.color(pal.WindowText) + self.current_fg = pal.color(pal.WindowText).getRgb()[:3] build_ansi_color_tables(opts) - f = QFont(opts.font_family) + self.current_font = f = QFont(opts.font_family) f.setPointSizeF(opts.font_size) self.setFont(f) self.font_metrics = fm = QFontMetrics(self.font()) @@ -139,7 +155,6 @@ class TerminalWidget(QWidget): return r = ev.region() p = QPainter(self) - p.setRenderHints(p.TextAntialiasing | p.Antialiasing) try: self.paint_cursor(p) @@ -158,6 +173,12 @@ class TerminalWidget(QWidget): x, y = wrap_cursor_position(self.cursor.x, self.cursor.y, len(self.line_positions), len(self.cell_positions)) r = QRect(self.cell_positions[x], self.line_positions[y], self.cell_width, self.cell_height) self.last_drew_cursor_at = x, y + line = self.linebuf[x] + colors = line.basic_cell_data(y)[2] + if colors & HAS_BG_MASK: + bg = as_color(colors >> COL_SHIFT, bg_color_table()) + if bg is not None: + painter.fillRect(r, QColor(*bg)) if self.hasFocus(): painter.fillRect(r, self.cursor_color) else: @@ -168,23 +189,18 @@ class TerminalWidget(QWidget): line = self.linebuf[row] ch, attrs, colors = line.basic_cell_data(col) x, y = self.cell_positions[col], self.line_positions[row] - if colors & HAS_BG_MASK: + if colors & HAS_BG_MASK and (col != self.last_drew_cursor_at[0] or row != self.last_drew_cursor_at[1]): bg = as_color(colors >> COL_SHIFT, bg_color_table()) if bg is not None: r = QRect(x, y, self.cell_width, self.cell_height) - painter.fillRect(r, bg) + painter.fillRect(r, QColor(*bg)) if ch == 0 or ch == 32: # An empty cell pass else: text = chr(ch) + line.combining_chars.get(col, '') - fg = as_color(colors & COL_MASK, fg_color_table()) - if fg is not None: - painter.save() - painter.setPen(QPen(fg)) - painter.drawText(x, y + self.baseline_offset, text) - if fg is not None: - painter.restore() + p = pixmap_for_text(text, colors, self.current_fg, self.current_font, self.cell_width * 2, self.cell_height, self.baseline_offset) + painter.drawPixmap(x, y, p) def keyPressEvent(self, ev): mods = ev.modifiers()