Speed up dirty_cells

This commit is contained in:
Kovid Goyal 2016-10-20 11:13:04 +05:30
parent eaa6c7656a
commit efeb2ebdaa
2 changed files with 34 additions and 33 deletions

View File

@ -2,7 +2,7 @@
# vim:fileencoding=utf-8 # vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from typing import Tuple, Iterator, Union, Sequence from typing import Tuple, Iterator, Sequence
from PyQt5.QtCore import pyqtSignal, QTimer, QRect, Qt 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
@ -10,7 +10,7 @@ from PyQt5.QtWidgets import QWidget
from .config import build_ansi_color_tables, Options, fg_color_table, bg_color_table from .config import build_ansi_color_tables, Options, fg_color_table, bg_color_table
from .data_types import Line, Cursor, HAS_BG_MASK, COL_SHIFT, COL_MASK, as_color from .data_types import Line, Cursor, HAS_BG_MASK, COL_SHIFT, COL_MASK, as_color
from .utils import set_current_font_metrics from .utils import set_current_font_metrics, first_intersecting_bucket, last_intersecting_bucket
from .tracker import ChangeTracker from .tracker import ChangeTracker
from .screen import wrap_cursor_position from .screen import wrap_cursor_position
from .keys import key_event_to_data from .keys import key_event_to_data
@ -124,23 +124,15 @@ class TerminalWidget(QWidget):
pass pass
self.update(reg) self.update(reg)
def dirty_lines(self, region: QRegion) -> Iterator[Tuple[int, QRegion]]: def dirty_cells(self, region: QRegion) -> Iterator[Tuple[int]]:
w = self.width() - 2 * self.hmargin for rect in region.rects():
for i, y in enumerate(self.line_positions): left, top, w, h = rect.getRect()
ir = region.intersected(QRect(self.hmargin, y, w, self.cell_height)) right, bottom = left + w, top + h
if not ir.isEmpty(): for lnum in range(min(0, first_intersecting_bucket(self.cell_height, top, self.vmargin)),
yield i, ir max(self.lines_per_screen - 1, last_intersecting_bucket(self.cell_height, bottom, self.vmargin))):
for cnum in range(min(0, first_intersecting_bucket(self.cell_width, left, self.hmargin)),
def dirty_cells(self, y: int, line_region: QRegion) -> Iterator[int]: max(self.cells_per_line - 1, last_intersecting_bucket(self.cell_width, right, self.hmargin))):
for i, x in enumerate(self.cell_positions): yield lnum, cnum
if line_region.intersects(QRect(x, y, self.cell_width, self.cell_height)):
yield i
def line(self, screen_line: int) -> Union[Line, None]:
try:
return self.linebuf[screen_line]
except IndexError:
pass
def paintEvent(self, ev): def paintEvent(self, ev):
if self.size() != self.layout_size: if self.size() != self.layout_size:
@ -155,18 +147,12 @@ class TerminalWidget(QWidget):
import traceback import traceback
traceback.print_exc() traceback.print_exc()
for lnum, line_region in self.dirty_lines(r): for lnum, cnum in self.dirty_cells(r):
line = self.line(lnum)
if line is not None:
ypos = self.line_positions[lnum]
for cnum in self.dirty_cells(ypos, line_region):
p.save()
try: try:
self.paint_cell(p, line, cnum, ypos) self.paint_cell(p, cnum, lnum)
except Exception: except Exception:
import traceback import traceback
traceback.print_exc() traceback.print_exc()
p.restore()
def paint_cursor(self, painter): def paint_cursor(self, painter):
x, y = wrap_cursor_position(self.cursor.x, self.cursor.y, len(self.line_positions), len(self.cell_positions)) x, y = wrap_cursor_position(self.cursor.x, self.cursor.y, len(self.line_positions), len(self.cell_positions))
@ -178,9 +164,10 @@ class TerminalWidget(QWidget):
painter.setPen(QPen(self.cursor_color)) painter.setPen(QPen(self.cursor_color))
painter.drawRect(r) painter.drawRect(r)
def paint_cell(self, painter: QPainter, line: Line, col: int, y: int) -> None: def paint_cell(self, painter: QPainter, col: int, row: int) -> None:
line = self.linebuf[row]
ch, attrs, colors = line.basic_cell_data(col) ch, attrs, colors = line.basic_cell_data(col)
x = self.cell_positions[col] x, y = self.cell_positions[col], self.line_positions[row]
if colors & HAS_BG_MASK: if colors & HAS_BG_MASK:
bg = as_color(colors >> COL_SHIFT, bg_color_table()) bg = as_color(colors >> COL_SHIFT, bg_color_table())
if bg is not None: if bg is not None:
@ -193,8 +180,11 @@ class TerminalWidget(QWidget):
text = chr(ch) + line.combining_chars.get(col, '') text = chr(ch) + line.combining_chars.get(col, '')
fg = as_color(colors & COL_MASK, fg_color_table()) fg = as_color(colors & COL_MASK, fg_color_table())
if fg is not None: if fg is not None:
painter.save()
painter.setPen(QPen(fg)) painter.setPen(QPen(fg))
painter.drawText(x, y + self.baseline_offset, text) painter.drawText(x, y + self.baseline_offset, text)
if fg is not None:
painter.restore()
def keyPressEvent(self, ev): def keyPressEvent(self, ev):
mods = ev.modifiers() mods = ev.modifiers()

View File

@ -3,6 +3,7 @@
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import os import os
import math
import sys import sys
import termios import termios
import struct import struct
@ -108,3 +109,13 @@ def timeit(name, do_timing=False):
yield yield
if do_timing: if do_timing:
print('Time for {}: {}'.format(name, monotonic() - st)) print('Time for {}: {}'.format(name, monotonic() - st))
def first_intersecting_bucket(sz, boundary, offset=0):
' Solve the eqn: offset + (n + 1) * sz >= boundary '
return int(math.ceil((boundary - offset) / sz)) - 1
def last_intersecting_bucket(sz, boundary, offset):
' Solve the eqn: offset + n * sz <= boundary '
return int(math.floor((boundary - offset) / sz))