From d07815a94f78fa5a3bfc9f02a16ba62c9854d439 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net>
Date: Sun, 3 Nov 2024 00:12:56 -0400
Subject: [PATCH] Implement double-height.

This is so gross... outputting any character depends on every
character above it, so it must be checked every time.  If the state
was ste up differently, this could be avoided, but it's not, so it
can't.

For v2, I'm going to want to store some per-line data in a separate
struct so I can track this.
---
 src/conio/bitmap_con.c | 86 +++++++++++++++++++++++++++++++++++++++++-
 src/conio/cterm.c      | 58 ++++++++++++++++++++++++----
 2 files changed, 135 insertions(+), 9 deletions(-)

diff --git a/src/conio/bitmap_con.c b/src/conio/bitmap_con.c
index 5ff88585df..4538d9b650 100644
--- a/src/conio/bitmap_con.c
+++ b/src/conio/bitmap_con.c
@@ -541,6 +541,9 @@ static int bitmap_draw_one_char(struct vmem_cell *vc, unsigned int xpos, unsigne
 	WORD	sch;
 	BOOL	draw_fg = TRUE;
 	size_t rsz;
+	bool double_height = false;
+	bool bottom = false;
+	bool top = false;
 
 	if(!bitmap_initialized) {
 		return(-1);
@@ -596,6 +599,58 @@ static int bitmap_draw_one_char(struct vmem_cell *vc, unsigned int xpos, unsigne
 
 	pixeloffset = PIXEL_OFFSET(screena, xoffset, yoffset);
 	rsz = screena.screenwidth - vstat.charwidth;
+	// PRESTEL!
+	if (vstat.mode == PRESTEL_40X24) {
+		struct vstat_vmem *vmem_ptr = get_vmem(&vstat);
+
+		if (ypos > 1) {
+			for (y = 0; y < ypos; y++) {
+				if (top) {
+					bottom = true;
+					top = false;
+				}
+				else {
+					if (bottom)
+						bottom = false;
+					else {
+						if (y == ypos - 1)
+							break;
+						for (x = 0; x < vstat.cols; x++) {
+							if (vmem_ptr->vmem[y * vstat.cols + x].bg & 0x01000000) {
+								top = true;
+								break;
+							}
+						}
+					}
+				}
+			}
+		}
+		if (bottom) {
+			if (vmem_ptr->vmem[(ypos - 2) * vstat.cols + (xpos - 1)].bg & 0x01000000) {
+				double_height = true;
+			}
+			fg = vmem_ptr->vmem[(ypos - 2) * vstat.cols + (xpos - 1)].fg;
+			bg = vmem_ptr->vmem[(ypos - 2) * vstat.cols + (xpos - 1)].bg;
+		}
+		else {
+			if (ypos != vstat.rows) {
+				if (vmem_ptr->vmem[(ypos - 1) * vstat.cols + (xpos - 1)].bg & 0x01000000) {
+					top = true;
+					double_height = true;
+				}
+			}
+		}
+		// Draw as space if not double-bottom
+		if (bottom) {
+			if (double_height) {
+				pixeloffset -= vstat.charheight * vstat.scrnwidth;
+				fontoffset=(vmem_ptr->vmem[(ypos - 2) * vstat.cols + (xpos - 1)].ch) * (vstat.charheight * ((fdw + 7) / 8));
+			}
+			else
+				fontoffset=(32) * (vstat.charheight * ((fdw + 7) / 8));
+		}
+		release_vmem(vmem_ptr);
+	}
 	for (y = 0; y < vstat.charheight; y++) {
 		for(x = 0; x < vstat.charwidth; x++) {
 			fdx = x;
@@ -624,12 +679,24 @@ static int bitmap_draw_one_char(struct vmem_cell *vc, unsigned int xpos, unsigne
 					screena.update_pixels = 1;
 					screena.rect->data[pixeloffset] = fg;
 				}
+				if (double_height) {
+					if (screena.rect->data[pixeloffset+screena.screenwidth] != fg) {
+						screena.update_pixels = 1;
+						screena.rect->data[pixeloffset+screena.screenwidth] = fg;
+					}
+				}
 			}
 			else {
 				if (screena.rect->data[pixeloffset] != bg) {
 					screena.update_pixels = 1;
 					screena.rect->data[pixeloffset] = bg;
 				}
+				if (double_height) {
+					if (screena.rect->data[pixeloffset+screena.screenwidth] != bg) {
+						screena.update_pixels = 1;
+						screena.rect->data[pixeloffset+screena.screenwidth] = bg;
+					}
+				}
 			}
 
 			if(fbb) {
@@ -637,18 +704,35 @@ static int bitmap_draw_one_char(struct vmem_cell *vc, unsigned int xpos, unsigne
 					screenb.update_pixels = 1;
 					screenb.rect->data[pixeloffset] = fg;
 				}
+				if (double_height) {
+					if (screenb.rect->data[pixeloffset+screena.screenwidth] != fg) {
+						screenb.update_pixels = 1;
+						screenb.rect->data[pixeloffset+screena.screenwidth] = fg;
+					}
+				}
 			}
 			else {
 				if (screenb.rect->data[pixeloffset] != bg) {
 					screenb.update_pixels = 1;
 					screenb.rect->data[pixeloffset] = bg;
 				}
+				if (double_height) {
+					if (screenb.rect->data[pixeloffset+screena.screenwidth] != bg) {
+						screenb.update_pixels = 1;
+						screenb.rect->data[pixeloffset+screena.screenwidth] = bg;
+					}
+				}
 			}
 			pixeloffset++;
 		}
-		if (x & 0x07)
+		if (x & 0x07) {
 			fontoffset++;
+			if (double_height && ((y & 1) == 0))
+				fontoffset--;
+		}
 		pixeloffset += rsz;
+		if (double_height)
+			pixeloffset += screena.screenwidth;
 	}
 	pthread_mutex_unlock(&screenlock);
 
diff --git a/src/conio/cterm.c b/src/conio/cterm.c
index 9c58a68ea1..f955a484c7 100644
--- a/src/conio/cterm.c
+++ b/src/conio/cterm.c
@@ -796,6 +796,10 @@ set_attr(struct cterminal *cterm, unsigned char colour, bool bg)
 		cterm->attr |= (colour & 0x07);
 	}
 	attr2palette(cterm->attr, bg ? NULL : &cterm->fg_color, bg ? &cterm->bg_color : NULL);
+	if (cterm->emulation == CTERM_EMULATION_PRESTEL) {
+		if (cterm->extattr & CTERM_EXTATTR_PRESTEL_DOUBLE_HEIGHT)
+			cterm->bg_color |= 0x01000000;
+	}
 	if (bg)
 		FREE_AND_NULL(cterm->bg_tc_str);
 	else
@@ -832,9 +836,12 @@ prestel_apply_ctrl_before(struct cterminal *cterm, uint8_t ch)
 		case 73: // Steady
 			cterm->attr &= 0x7f;
 			attr2palette(cterm->attr, &cterm->fg_color, &cterm->bg_color);
+			if (CTERM_EXTATTR_PRESTEL_DOUBLE_HEIGHT)
+				cterm->bg_color |= 0x01000000;
 			break;
 		case 76: // Normal Height
 			cterm->extattr &= ~(CTERM_EXTATTR_PRESTEL_DOUBLE_HEIGHT);
+			cterm->bg_color &= ~0x01000000;
 			cterm->prestel_last_mosaic = 32;
 			break;
 		case 88: // Conceal Display
@@ -908,9 +915,12 @@ prestel_apply_ctrl_after(struct cterminal *cterm, uint8_t ch)
 		case 72: // Flash
 			cterm->attr |= 0x80;
 			attr2palette(cterm->attr, &cterm->fg_color, &cterm->bg_color);
+			if (cterm->extattr & CTERM_EXTATTR_PRESTEL_DOUBLE_HEIGHT)
+				cterm->bg_color |= 0x01000000;
 			break;
 		case 77: // Double Height
 			cterm->extattr |= CTERM_EXTATTR_PRESTEL_DOUBLE_HEIGHT;
+			cterm->bg_color |= 0x01000000;
 			cterm->prestel_last_mosaic = 32;
 			break;
 		case 80: // Mosaic Black
@@ -4948,7 +4958,7 @@ void cterm_start(struct cterminal *cterm)
  * - Format effector added or removed
  * - Hold mosaic character changed
  */
-static void prestel_fix_line(struct cterminal *cterm, int x, int y)
+static void prestel_fix_line(struct cterminal *cterm, int x, int y, bool restore, bool force)
 {
 	int sy = y;
 	int sx = 1;
@@ -4960,6 +4970,7 @@ static void prestel_fix_line(struct cterminal *cterm, int x, int y)
 	unsigned char attr = cterm->attr;
 	uint8_t prestel_last_mosaic = cterm->prestel_last_mosaic;
 	bool fixed = false;
+	bool fixedheight = false;
 
 	coord_conv_xy(cterm, CTERM_COORD_TERM, CTERM_COORD_SCREEN, &sy, &sx);
 	ex = sx + TERM_MAXX - 1;
@@ -4973,6 +4984,18 @@ static void prestel_fix_line(struct cterminal *cterm, int x, int y)
 			// This is a control character
 			ch = (line[i].fg & 0x7F000000) >> 24;
 			prestel_apply_ctrl_before(cterm, ch);
+			if ((cterm->extattr & CTERM_EXTATTR_PRESTEL_DOUBLE_HEIGHT) && ((line[i].bg & 0x01000000) == 0)) {
+				// Should be double-high
+				line[i].bg |= 0x01000000;
+				fixed = true;
+				fixedheight = true;
+			}
+			if (((cterm->extattr & CTERM_EXTATTR_PRESTEL_DOUBLE_HEIGHT) == 0) && (line[i].bg & 0x01000000)) {
+				// Should not be double-high
+				line[i].bg &= ~0x01000000;
+				fixed = true;
+				fixedheight = true;
+			}
 			if (line[i].fg != (cterm->fg_color | (ch << 24))
 			    || line[i].bg != cterm->bg_color
 			    || line[i].legacy_attr != cterm->attr) {
@@ -5009,6 +5032,18 @@ static void prestel_fix_line(struct cterminal *cterm, int x, int y)
 				prestel_last_mosaic = cterm->prestel_last_mosaic;
 			}
 			// This is displayable
+			if ((cterm->extattr & CTERM_EXTATTR_PRESTEL_DOUBLE_HEIGHT) && ((line[i].bg & 0x01000000) == 0)) {
+				// Should be double-high
+				line[i].bg |= 0x01000000;
+				fixed = true;
+				fixedheight = true;
+			}
+			if (((cterm->extattr & CTERM_EXTATTR_PRESTEL_DOUBLE_HEIGHT) == 0) && (line[i].bg & 0x01000000)) {
+				// Should not be double-high
+				line[i].bg &= ~0x01000000;
+				fixed = true;
+				fixedheight = true;
+			}
 			if (line[i].fg != cterm->fg_color
 			    || line[i].bg != cterm->bg_color
 			    || line[i].legacy_attr != cterm->attr) {
@@ -5052,14 +5087,19 @@ static void prestel_fix_line(struct cterminal *cterm, int x, int y)
 			}
 		}
 	}
-	if (fixed)
+	if (force || fixed)
 		vmem_puttext(sx, sy, ex, sy, line);
 	free(line);
-	cterm->extattr = extattr;
-	cterm->fg_color = fg_color;
-	cterm->bg_color = bg_color;
-	cterm->attr = attr;
-	cterm->prestel_last_mosaic = prestel_last_mosaic;
+	if (restore) {
+		cterm->extattr = extattr;
+		cterm->fg_color = fg_color;
+		cterm->bg_color = bg_color;
+		cterm->attr = attr;
+		cterm->prestel_last_mosaic = prestel_last_mosaic;
+	}
+	if (fixedheight) {
+		prestel_fix_line(cterm, x, y+1, false, true);
+	}
 }
 
 static void
@@ -5070,7 +5110,7 @@ advance_char(struct cterminal *cterm, int *x, int *y, int move)
 	int bm = cterm->bottom_margin;
 
 	if (cterm->emulation == CTERM_EMULATION_PRESTEL) {
-		prestel_fix_line(cterm, *x, *y);
+		prestel_fix_line(cterm, *x, *y, true, false);
 		TEXTATTR(cterm->attr);
 		setcolour(cterm->fg_color, cterm->bg_color);
 	}
@@ -5199,6 +5239,8 @@ ctputs(struct cterminal *cterm, char *buf)
 		}
 	}
 	CPUTS(outp);
+	if (cterm->emulation == CTERM_EMULATION_PRESTEL)
+		prestel_fix_line(cterm, cx, cy, true, false);
 	*cterm->_wscroll=oldscroll;
 }
 
-- 
GitLab