Fix double height character rendering logic
This commit is contained in:
@@ -215,13 +215,27 @@ class TeletextCanvas(QWidget):
|
|||||||
if 0 <= p.row <= 25:
|
if 0 <= p.row <= 25:
|
||||||
grid[p.row] = p
|
grid[p.row] = p
|
||||||
|
|
||||||
|
# Pass 1: Backgrounds
|
||||||
|
occlusion_mask = [False] * 40
|
||||||
for r in range(25):
|
for r in range(25):
|
||||||
packet = grid[r]
|
packet = grid[r]
|
||||||
self.draw_row(painter, r, packet)
|
occlusion_mask = self.draw_row(painter, r, packet, draw_bg=True, draw_fg=False, occlusion_mask=occlusion_mask)
|
||||||
|
|
||||||
|
# Pass 2: Foregrounds
|
||||||
|
occlusion_mask = [False] * 40
|
||||||
|
for r in range(25):
|
||||||
|
packet = grid[r]
|
||||||
|
occlusion_mask = self.draw_row(painter, r, packet, draw_bg=False, draw_fg=True, occlusion_mask=occlusion_mask)
|
||||||
|
|
||||||
painter.end()
|
painter.end()
|
||||||
|
|
||||||
def draw_row(self, painter, row, packet):
|
def draw_row(self, painter, row, packet, draw_bg=True, draw_fg=True, occlusion_mask=None):
|
||||||
|
if occlusion_mask is None:
|
||||||
|
occlusion_mask = [False] * 40
|
||||||
|
|
||||||
|
# Output mask for the next row
|
||||||
|
next_occlusion_mask = [False] * 40
|
||||||
|
|
||||||
# Default State at start of row
|
# Default State at start of row
|
||||||
fg = COLORS[7] # White
|
fg = COLORS[7] # White
|
||||||
bg = COLORS[0] # Black
|
bg = COLORS[0] # Black
|
||||||
@@ -229,6 +243,7 @@ class TeletextCanvas(QWidget):
|
|||||||
contiguous = True # Mosaic
|
contiguous = True # Mosaic
|
||||||
hold_graphics = False
|
hold_graphics = False
|
||||||
held_char = 0x20 # Space
|
held_char = 0x20 # Space
|
||||||
|
double_height = False
|
||||||
|
|
||||||
y = row * self.cell_h
|
y = row * self.cell_h
|
||||||
|
|
||||||
@@ -248,6 +263,24 @@ class TeletextCanvas(QWidget):
|
|||||||
for c in range(40):
|
for c in range(40):
|
||||||
x = c * self.cell_w
|
x = c * self.cell_w
|
||||||
|
|
||||||
|
# If this cell is occluded by the row above, skip drawing and attribute processing?
|
||||||
|
# Spec says "The characters in the row below are ignored."
|
||||||
|
# Ideally we shouldn't even process attributes, but for simple renderer we just skip draw.
|
||||||
|
# However, if we skip attribute processing, state (fg/bg) won't update.
|
||||||
|
# Teletext attributes are serial.
|
||||||
|
# BUT, if the row above covers it, the viewer sees the row above.
|
||||||
|
# Does the hidden content affect the *rest* of the row?
|
||||||
|
# Likely yes, attributes usually propagate.
|
||||||
|
# But the spec says "ignored". Let's assume we skip *everything* for this cell visually,
|
||||||
|
# but maybe we should technically maintain state?
|
||||||
|
# For "Double Height" visual correctness, skipping drawing is the key.
|
||||||
|
# We will Process attributes (to keep state consistent) but Skip Drawing if occluded.
|
||||||
|
|
||||||
|
# Wait, if we process attributes, we might set double_height=True for the NEXT row?
|
||||||
|
# If this cell is occluded, it shouldn't trigger DH for the next row.
|
||||||
|
|
||||||
|
is_occluded = occlusion_mask[c]
|
||||||
|
|
||||||
# Decide byte value
|
# Decide byte value
|
||||||
if row == 0 and c < 8:
|
if row == 0 and c < 8:
|
||||||
# Use generated header prefix
|
# Use generated header prefix
|
||||||
@@ -274,9 +307,9 @@ class TeletextCanvas(QWidget):
|
|||||||
elif byte_val == 0x1D: # New BG
|
elif byte_val == 0x1D: # New BG
|
||||||
bg = fg
|
bg = fg
|
||||||
elif byte_val == 0x0C: # Normal Height
|
elif byte_val == 0x0C: # Normal Height
|
||||||
pass
|
double_height = False
|
||||||
elif byte_val == 0x0D: # Double Height
|
elif byte_val == 0x0D: # Double Height
|
||||||
pass # Not implemented yet
|
double_height = True
|
||||||
elif byte_val == 0x19: # Contiguous Graphics
|
elif byte_val == 0x19: # Contiguous Graphics
|
||||||
contiguous = True
|
contiguous = True
|
||||||
elif byte_val == 0x1A: # Separated Graphics
|
elif byte_val == 0x1A: # Separated Graphics
|
||||||
@@ -286,14 +319,33 @@ class TeletextCanvas(QWidget):
|
|||||||
elif byte_val == 0x1F: # Release Graphics
|
elif byte_val == 0x1F: # Release Graphics
|
||||||
hold_graphics = False
|
hold_graphics = False
|
||||||
|
|
||||||
|
# Record Double Height for next row
|
||||||
|
if double_height and not is_occluded:
|
||||||
|
next_occlusion_mask[c] = True
|
||||||
|
|
||||||
|
# If occluded, do not draw anything for this cell
|
||||||
|
if is_occluded:
|
||||||
|
continue
|
||||||
|
|
||||||
# Draw Background
|
# Draw Background
|
||||||
painter.fillRect(x, y, self.cell_w, self.cell_h, bg)
|
if draw_bg:
|
||||||
|
# If double height, draw taller background
|
||||||
|
h_bg = self.cell_h * 2 if double_height else self.cell_h
|
||||||
|
painter.fillRect(x, y, self.cell_w, h_bg, bg)
|
||||||
|
|
||||||
# Draw Foreground
|
# Draw Foreground
|
||||||
|
if draw_fg:
|
||||||
|
# Calculate height
|
||||||
|
# For Mosaics, we use the height param.
|
||||||
|
# For Alphanumerics, we scale the painter.
|
||||||
|
|
||||||
if is_control:
|
if is_control:
|
||||||
# "Set-at" spacing attribute? Teletext control codes occupy a space
|
# "Set-at" spacing attribute? Teletext control codes occupy a space
|
||||||
# unless "Hold Graphics" replaces it with previous graphic char.
|
# unless "Hold Graphics" replaces it with previous graphic char.
|
||||||
if hold_graphics and graphics_mode:
|
if hold_graphics and graphics_mode:
|
||||||
|
if double_height:
|
||||||
|
self.draw_mosaic(painter, x, y, held_char, fg, contiguous, height=self.cell_h * 2)
|
||||||
|
else:
|
||||||
self.draw_mosaic(painter, x, y, held_char, fg, contiguous)
|
self.draw_mosaic(painter, x, y, held_char, fg, contiguous)
|
||||||
else:
|
else:
|
||||||
# Draw space (nothing, since we filled BG)
|
# Draw space (nothing, since we filled BG)
|
||||||
@@ -301,28 +353,45 @@ class TeletextCanvas(QWidget):
|
|||||||
else:
|
else:
|
||||||
if graphics_mode:
|
if graphics_mode:
|
||||||
# Mosaic Graphics
|
# Mosaic Graphics
|
||||||
|
h_mos = self.cell_h * 2 if double_height else self.cell_h
|
||||||
if (0x20 <= byte_val <= 0x3F) or (0x60 <= byte_val <= 0x7F):
|
if (0x20 <= byte_val <= 0x3F) or (0x60 <= byte_val <= 0x7F):
|
||||||
self.draw_mosaic(painter, x, y, byte_val, fg, contiguous)
|
self.draw_mosaic(painter, x, y, byte_val, fg, contiguous, height=h_mos)
|
||||||
held_char = byte_val
|
held_char = byte_val
|
||||||
else:
|
else:
|
||||||
# Capital letter in graphics mode? Usually shows char?
|
# Capital letter in graphics mode? Usually shows char?
|
||||||
char = get_char(byte_val, self.subset_idx)
|
char = get_char(byte_val, self.subset_idx)
|
||||||
painter.setPen(fg)
|
painter.setPen(fg)
|
||||||
|
if double_height:
|
||||||
|
painter.save()
|
||||||
|
painter.translate(x, y)
|
||||||
|
painter.scale(1, 2)
|
||||||
|
painter.drawText(QRect(0, 0, self.cell_w, self.cell_h), Qt.AlignmentFlag.AlignCenter, char)
|
||||||
|
painter.restore()
|
||||||
|
else:
|
||||||
painter.drawText(QRect(x, y, self.cell_w, self.cell_h), Qt.AlignmentFlag.AlignCenter, char)
|
painter.drawText(QRect(x, y, self.cell_w, self.cell_h), Qt.AlignmentFlag.AlignCenter, char)
|
||||||
held_char = 0x20
|
held_char = 0x20
|
||||||
else:
|
else:
|
||||||
# Alphanumeric
|
# Alphanumeric
|
||||||
char = get_char(byte_val, self.subset_idx)
|
char = get_char(byte_val, self.subset_idx)
|
||||||
painter.setPen(fg)
|
painter.setPen(fg)
|
||||||
|
if double_height:
|
||||||
|
painter.save()
|
||||||
|
painter.translate(x, y)
|
||||||
|
painter.scale(1, 2)
|
||||||
|
painter.drawText(QRect(0, 0, self.cell_w, self.cell_h), Qt.AlignmentFlag.AlignCenter, char)
|
||||||
|
painter.restore()
|
||||||
|
else:
|
||||||
painter.drawText(QRect(x, y, self.cell_w, self.cell_h), Qt.AlignmentFlag.AlignCenter, char)
|
painter.drawText(QRect(x, y, self.cell_w, self.cell_h), Qt.AlignmentFlag.AlignCenter, char)
|
||||||
# Draw Cursor
|
# Draw Cursor
|
||||||
# Invert the cell at cursor position
|
# Invert the cell at cursor position
|
||||||
if self.cursor_visible and c == self.cursor_x and row == self.cursor_y:
|
if draw_fg and self.cursor_visible and c == self.cursor_x and row == self.cursor_y:
|
||||||
painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Difference)
|
painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_Difference)
|
||||||
# Difference with white creates inversion
|
# Difference with white creates inversion
|
||||||
|
# Note: Cursor follows double height? Probably just the active cell.
|
||||||
painter.fillRect(x, y, self.cell_w, self.cell_h, QColor(255, 255, 255))
|
painter.fillRect(x, y, self.cell_w, self.cell_h, QColor(255, 255, 255))
|
||||||
painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_SourceOver)
|
painter.setCompositionMode(QPainter.CompositionMode.CompositionMode_SourceOver)
|
||||||
|
|
||||||
|
return next_occlusion_mask
|
||||||
def mousePressEvent(self, event):
|
def mousePressEvent(self, event):
|
||||||
self.setFocus()
|
self.setFocus()
|
||||||
# Calculate cell from mouse position
|
# Calculate cell from mouse position
|
||||||
@@ -360,7 +429,10 @@ class TeletextCanvas(QWidget):
|
|||||||
row = int(my / (self.cell_h * scale))
|
row = int(my / (self.cell_h * scale))
|
||||||
self.set_cursor(col, row)
|
self.set_cursor(col, row)
|
||||||
|
|
||||||
def draw_mosaic(self, painter, x, y, char_code, color, contiguous):
|
def draw_mosaic(self, painter, x, y, char_code, color, contiguous, height=None):
|
||||||
|
if height is None:
|
||||||
|
height = self.cell_h
|
||||||
|
|
||||||
val = char_code & 0x7F
|
val = char_code & 0x7F
|
||||||
bits = 0
|
bits = 0
|
||||||
if val >= 0x20:
|
if val >= 0x20:
|
||||||
@@ -368,7 +440,7 @@ class TeletextCanvas(QWidget):
|
|||||||
|
|
||||||
# Grid definitions for 2x3 grid
|
# Grid definitions for 2x3 grid
|
||||||
x_splits = [0, int(self.cell_w / 2), self.cell_w]
|
x_splits = [0, int(self.cell_w / 2), self.cell_w]
|
||||||
y_splits = [0, int(self.cell_h / 3), int(2 * self.cell_h / 3), self.cell_h]
|
y_splits = [0, int(height / 3), int(2 * height / 3), height]
|
||||||
|
|
||||||
# Block indices (col, row) for the 6 bits
|
# Block indices (col, row) for the 6 bits
|
||||||
block_indices = [
|
block_indices = [
|
||||||
|
|||||||
Reference in New Issue
Block a user