Use a rendered text cache for faster painting

This commit is contained in:
Kovid Goyal
2016-10-20 12:00:14 +05:30
parent efeb2ebdaa
commit 37ac5d4194
3 changed files with 35 additions and 21 deletions

View File

@@ -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)

View File

@@ -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:

View File

@@ -2,10 +2,11 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
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()