From 634c876326da39e98dda7cbb3542425b62cc59d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net>
Date: Mon, 10 May 2021 12:18:39 -0400
Subject: [PATCH] Numerous changes:

Clamp coordinate translation max
Fill using native pattern, not scaled pattern
Add a bad, broken, and slow even/odd fill
Fix logging when RIP is enabled
Use the same ellipse algorithm for partial as full ellipses
Detect diagonal movement in ellipse drawing and don't make the edge fat
Only recurse once in the flood fill
Implement the v2 poly bezier command
Enable filled pie slices
Add RIP_POINT command (basically a scaled pixel using line width)
---
 src/conio/bitmap_con.c |   4 +-
 src/syncterm/bbslist.c |   2 +-
 src/syncterm/ripper.c  | 601 ++++++++++++++++++++++++++++++++---------
 3 files changed, 482 insertions(+), 125 deletions(-)

diff --git a/src/conio/bitmap_con.c b/src/conio/bitmap_con.c
index 71b44795e6..9187c52a79 100644
--- a/src/conio/bitmap_con.c
+++ b/src/conio/bitmap_con.c
@@ -434,8 +434,8 @@ static uint32_t color_value(uint32_t col)
 {
 	if (col & 0x80000000)
 		return col;
-	if (col < sizeof(palette) / sizeof(palette[0]))
-		return palette[col];
+	if ((col & 0xffffff) < sizeof(palette) / sizeof(palette[0]))
+		return (col & 0xff000000) | palette[col & 0xffffff];
 	fprintf(stderr, "Invalid colour value: %08x\n", col);
 	return 0xff000000;
 }
diff --git a/src/syncterm/bbslist.c b/src/syncterm/bbslist.c
index f15c9ad354..0a724c2548 100644
--- a/src/syncterm/bbslist.c
+++ b/src/syncterm/bbslist.c
@@ -921,7 +921,7 @@ get_rip_version(int oldver, int *changed)
 	uifc.helpbuf=   "`RIP Version`\n\n"
 	    "RIP v1 requires EGA mode while RIP v3\n"
 	    "works in any screen mode.";
-	switch(uifc.list(WIN_SAV,0,0,0,&cur,&bar,"RIP Mode",rip_versions)) {
+	switch(uifc.list(WIN_SAV,0,0,0,&cur,NULL,"RIP Mode",rip_versions)) {
 		case -1:
 			check_exit(FALSE);
 			break;
diff --git a/src/syncterm/ripper.c b/src/syncterm/ripper.c
index 83b8475e81..b113aef29f 100644
--- a/src/syncterm/ripper.c
+++ b/src/syncterm/ripper.c
@@ -7773,8 +7773,11 @@ map_rip_x(int x)
 			dx /= rip.x_dim;
 			return roundl(dx);
 		}
-		for (i = 0; i < rip.x_dim; i++)
+		for (i = 0; i < rip.x_dim; i++) {
 			rip.xmap[i] = roundl(((double)i) * rip.x_max / rip.x_dim);
+			if (rip.xmap[i] >= rip.x_max)
+				rip.xmap[i] = rip.x_max - 1;
+		}
 	}
 	if (x >= rip.x_dim)
 		x = rip.x_dim;
@@ -7801,8 +7804,11 @@ map_rip_y(int y)
 			dy /= rip.y_dim;
 			return roundl(dy);
 		}
-		for (i = 0; i < rip.y_dim; i++)
+		for (i = 0; i < rip.y_dim; i++) {
 			rip.ymap[i] = roundl(((double)i) * rip.y_max / rip.y_dim);
+			if (rip.ymap[i] >= rip.y_max)
+				rip.ymap[i] = rip.y_max - 1;
+		}
 	}
 	if (y >= rip.y_dim)
 		y = rip.y_dim;
@@ -8107,9 +8113,9 @@ next_char(const char *buf, size_t *pos)
 		else
 			return (uint8_t)buf[*pos];
 	}
-	if (buf[*pos] == '!')
+	/*if (buf[*pos] == '!')
 		return -1;
-	else if (buf[*pos] == '|')
+	else */if (buf[*pos] == '|')
 		return -1;
 	else if (buf[*pos] == '\r')
 		return -1;
@@ -8179,6 +8185,15 @@ set_pixel(int x, int y, uint32_t fg)
 	scale_setpixel(rip.viewport.sx + x, rip.viewport.sy + y, fg);
 }
 
+static void
+native_fill_pixel(int x, int y)
+{
+	if (rip.fill_pattern[y & 0x07] & (0x80 >> (x & 0x07)))
+		setpixel(x, y, map_rip_color(rip.fill_color));
+	else
+		setpixel(x, y, map_rip_color(0));
+}
+
 static void
 fill_pixel(int x, int y)
 {
@@ -8187,10 +8202,10 @@ fill_pixel(int x, int y)
 	if (x > rip.viewport.ex || y > rip.viewport.ey)
 		return;
 
-	if (rip.fill_pattern[y & 0x07] & (0x80 >> (x & 0x07)))
-		rip_setpixel(x, y, rip.fill_color);
-	else
-		rip_setpixel(x, y, 0);
+	// Handle the translation here for nicer fills...
+	x = map_rip_x(x);
+	y = map_rip_y(y);
+	native_fill_pixel(x, y);
 }
 
 static int
@@ -8599,6 +8614,7 @@ set_line(int x1, int y1, int x2, int y2, uint32_t color, uint16_t pat, int width
 	int *maxc, *maxs, *maxe, *maxo, *maxd;
 	int x, y;
 	int swap = 0;
+	int lx, ly;
 
 	int dx = abs(x2 - x1);
 	int dy = abs(y2 - y1);
@@ -8665,6 +8681,16 @@ set_line(int x1, int y1, int x2, int y2, uint32_t color, uint16_t pat, int width
 		if (pat & (0x8000 >> ppos)) {
 			set_pixel(x, y, color);
 		}
+		else {
+			// Set fill border...
+			if (color & 0x40000000) {
+				lx = map_rip_x(x);
+				ly = map_rip_y(y);
+				struct ciolib_pixels *pixels = getpixels(lx, ly, lx, ly, false);
+				set_pixel(x, y, pixels->pixels[0] | 0x40000000);
+				freepixels(pixels);
+			}
+		}
 		if (de >= 0) {
 			*minc = (*minc) + *mino;
 			de = de - ((2 * *maxd) & INT_MAX);
@@ -8790,6 +8816,160 @@ static void invert_rect(int x1, int y1, int x2, int y2)
 	freepixels(pix);
 }
 
+static void do_fill(bool overwrite)
+{
+	struct ciolib_pixels *pix;
+	int x, cx, y;
+	int pixel;
+	bool fill = false;
+	bool in_line = false;
+	uint32_t col;
+
+	pix = getpixels(0, 0, rip.x_max - 1, rip.y_max - 1, false);
+	FREE_AND_NULL(pix->pixelsb);
+	if (pix == NULL)
+		return;
+
+	// Horizontal even-odd top-left to bottom-right.
+	pixel = 0;
+	for (y = 0; y < pix->height; y++) {
+		in_line = fill = false;
+		for (x = 0; x < pix->width; x++) {
+			if (in_line) {
+				if (!(pix->pixels[pixel] & 0x40000000)) {
+					fill = !fill;
+					in_line = false;
+				}
+			}
+			else {
+				if (pix->pixels[pixel] & 0x40000000) {
+					in_line = true;
+				}
+			}
+			if (fill & !in_line)
+				pix->pixels[pixel] |= 0x20000000;
+			pixel++;
+		}
+	}
+
+	// Horizontal even-odd bottom-right to top-left
+	pixel = pix->height * pix->width - 1;
+	for (y = 0; y < pix->height; y++) {
+		in_line = fill = false;
+		for (x = 0; x < pix->width; x++) {
+			if (in_line) {
+				if (!(pix->pixels[pixel] & 0x40000000)) {
+					fill = !fill;
+					in_line = false;
+				}
+			}
+			else {
+				if (pix->pixels[pixel] & 0x40000000) {
+					in_line = true;
+				}
+			}
+			if (fill & !in_line)
+				pix->pixels[pixel] |= 0x10000000;
+			pixel--;
+		}
+	}
+
+	// Vertical even-odd top-left to bottom-right.
+	for (x = 0; x < pix->width; x++) {
+		pixel = x;
+		in_line = fill = false;
+		for (y = 0; y < pix->height; y++) {
+			if (in_line) {
+				if (!(pix->pixels[pixel] & 0x40000000)) {
+					fill = !fill;
+					in_line = false;
+				}
+			}
+			else {
+				if (pix->pixels[pixel] & 0x40000000) {
+					in_line = true;
+				}
+			}
+			if (fill & !in_line)
+				pix->pixels[pixel] |= 0x08000000;
+			pixel += pix->width;
+		}
+	}
+
+	// Vertical even-odd bottom-right to top-left
+	for (x = 0; x < pix->width; x++) {
+		pixel = (pix->height * pix->width) - x - 1;
+		in_line = fill = false;
+		for (y = 0; y < pix->height; y++) {
+			if (in_line) {
+				if (!(pix->pixels[pixel] & 0x40000000)) {
+					fill = !fill;
+					in_line = false;
+				}
+			}
+			else {
+				if (pix->pixels[pixel] & 0x40000000) {
+					in_line = true;
+				}
+			}
+			if (fill & !in_line)
+				pix->pixels[pixel] |= 0x04000000;
+			pixel -= pix->width;
+		}
+	}
+
+	// Horizontal gap-fill
+	pixel = 0;
+	for (y = 0; y < pix->height; y++) {
+		pixel++;
+		for (x = 1; x < pix->width - 1; x++) {
+			int filled = pix->pixels[pixel] & 0x3c000000;
+			if (filled && filled != 0x3c000000) {
+				if (((pix->pixels[pixel - 1] & 0xff000000) == 0x3c000000)
+				    || ((pix->pixels[pixel + 1] & 0xff000000) == 0x3c000000))
+					pix->pixels[pixel] |= 0x3c000000;
+			}
+			pixel++;
+		}
+		pixel++;
+	}
+
+	// Vertical gap-fill
+	for (x = 0; x < pix->width; x++) {
+		pixel = x;
+		pixel += pix->width;
+		for (y = 1; y < pix->height - 1; y++) {
+			int filled = pix->pixels[pixel] & 0x3c000000;
+			if (filled && filled != 0x3c000000) {
+				if (((pix->pixels[pixel - pix->width] & 0xff000000) == 0x3c000000)
+				    || ((pix->pixels[pixel + pix->width] & 0xff000000) == 0x3c000000))
+					pix->pixels[pixel] |= 0x3c000000;
+			}
+			pixel += pix->width;
+		}
+		pixel += pix->width;
+	}
+
+	// Update the screen
+	pixel = 0;
+	for (y = 0; y < pix->height; y++) {
+		for (x = 0; x < pix->width; x++) {
+			switch ((pix->pixels[pixel] >> 26) & 0x1f) {
+				case 0:
+					break;
+				case 15:
+					native_fill_pixel(x, y);
+					break;
+				case 16:
+					setpixel(x, y, pix->pixels[pixel] & 0x80ffffff);
+					break;
+			}
+			pixel++;
+		}
+	}
+	freepixels(pix);
+}
+
 /*
  * TODO: Not sure why I need to subtract one from x2/y2 here...
  */
@@ -9568,6 +9748,8 @@ reinit_screen(uint8_t *font, int fx, int fy)
 	void *nvmem;
 
 	hold_update = 0;
+	cterm->logfile = NULL;
+	cterm->log = CTERM_LOG_NONE;
 	cterm_end(cterm);
 	normal_palette();
 	// TODO: You know this is insane right?
@@ -9619,6 +9801,8 @@ reinit_screen(uint8_t *font, int fx, int fy)
 	cterm->bg_color = oldcterm.bg_color - 16;
 	cterm->fg_color = oldcterm.fg_color - 16;
 	cterm_start(cterm);
+	cterm->logfile = oldcterm.logfile;
+	cterm->log = oldcterm.log;
 	shadow_palette();
 	set_ega_palette();
 	attr2palette(cterm->attr, &cterm->fg_color, &cterm->bg_color);
@@ -9631,31 +9815,12 @@ reinit_screen(uint8_t *font, int fx, int fy)
 	freepixels(pix);
 }
 
-static void
-draw_ellipse(int x1, int y1, int arg1, int arg2, int x2, int y2)
-{
-	int arg3;
-
-	if (arg1 < 0 || arg2 < 0 || x2 < 0 || y2 < 0)
-		return;
-	if (arg2 < arg1)
-		arg2 += 360;
-	// TODO: Use 'o' command ellipse when possible...
-	for (arg3 = arg1; arg3 <= arg2; arg3++) {
-		set_line(x1 + nearbyint(cos((M_PI / 180.0) * arg3) * x2),
-		    y1 - nearbyint(sin((M_PI / 180.0) * arg3) * y2),
-		    x1 + nearbyint(cos((1.0 + arg3) * (M_PI / 180.0)) * x2),
-		    y1 - nearbyint(sin((1.0 + arg3) * (M_PI / 180.0)) * y2),
-		    map_rip_color(rip.color), 0xffff, rip.line_width);
-	}
-}
-
 // M. Douglas McIlroy
 // There Is No Royal Road to Programs - A Trilogy on Raster Ellipses and Programming Methodology
 #define incx() x++, dxt += d2xt, t += dxt
 #define incy() y--, dyt += d2yt, t += dyt
 static void
-full_ellipse(int xc, int yc, int a, int b, bool fill)
+full_ellipse(int xc, int yc, int sa, int ea, int a, int b, bool fill, uint32_t colour)
 {
 	if (b == 0) {
 		b = 1;
@@ -9675,9 +9840,21 @@ full_ellipse(int xc, int yc, int a, int b, bool fill)
 	int fy;
 	bool skip = false;
 
+	bool partial = true;
+	if (ea != sa && ((ea + 360) % 360) == ((sa + 360) % 360))
+		partial = false;
+
+	double angle;
+	double qangle;
+
 	while(y>=0 && x<=a) {
+		angle = atan((x * (M_PI / 180.0)) / (y *(M_PI / 180.0)));
+		angle /= (M_PI / 180.0);
+		angle = 90 - angle;
 		if (!skip) {
 			if(x!=0 || y!=0) {
+				// Top-left quadrant.
+				qangle = 180 - angle;
 				if (fill) {
 					if (x == 0) {
 						for (fy = yc - y + 1; fy < yc + y; fy++) {
@@ -9685,8 +9862,10 @@ full_ellipse(int xc, int yc, int a, int b, bool fill)
 						}
 					}
 				}
-				if (rip.color)
-					draw_pixel(xc-x, yc-y);
+				if (rip.borders) {
+					if (sa < qangle && ea > qangle)
+						set_pixel(xc-x, yc-y, colour);
+				}
 			}
 			if(x!=0 && y!=0) {
 				if (fill) {
@@ -9695,14 +9874,22 @@ full_ellipse(int xc, int yc, int a, int b, bool fill)
 						fill_pixel(xc + x, fy);
 					}
 				}
-				if (rip.color) {
-					if (rip.borders) {
-						draw_pixel(xc+x, yc-y);
-						draw_pixel(xc-x, yc+y);
-					}
+				if (rip.borders) {
+					// Top-right quadrant.
+					if (sa < angle && ea > angle)
+						set_pixel(xc+x, yc-y, colour);
+					// Bottom-left quadrant.
+					qangle = 180 + angle;
+					if (sa < qangle && ea > qangle)
+						set_pixel(xc-x, yc+y, colour);
 				}
 			}
-			draw_pixel(xc+x, yc+y);
+			// Bottom-right quadrant
+			qangle = 360 - angle;
+			if (rip.borders) {
+				if (sa < qangle && ea > qangle)
+					set_pixel(xc+x, yc+y, colour);
+			}
 		}
 		skip = false;
 		if(t + b2*x <= crit1 ||   /* e(x+1,y-1/2) <= 0 */
@@ -9712,17 +9899,21 @@ full_ellipse(int xc, int yc, int a, int b, bool fill)
 			if (!(t + b2*x <= crit1 || t+a2*y <= crit3) && (t - a2*y > crit2))
 				skip = true;
 		}
-		else if(t - a2*y > crit2) /* e(x+1/2,y-1) > 0 */
+		else if(t - a2*y > crit2) { /* e(x+1/2,y-1) > 0 */
 			incy();
+			// Angle move, skip next...
+			if (t + b2*x <= crit1 || t+a2*y <= crit3)
+				skip = true;
+		}
 		else {
 			incx();
 			incy();
 		}
 	}
-	if (rip.line_width == 3 && rip.color) {
+	if (rip.line_width == 3 && rip.borders) {
 		rip.line_width = 1;
-		full_ellipse(xc, yc, a - 1, b - 1, false);
-		full_ellipse(xc, yc, a + 1, b + 1, false);
+		full_ellipse(xc, yc, sa, ea, a - 1, b - 1, false, colour & 0xcfffffff);
+		full_ellipse(xc, yc, sa, ea, a + 1, b + 1, false, colour & 0xcfffffff);
 		rip.line_width = 3;
 	}
 }
@@ -9730,17 +9921,19 @@ full_ellipse(int xc, int yc, int a, int b, bool fill)
 struct saved_point {
 	int x;
 	int y;
+	int oy;
 	struct saved_point *next;
 };
 
 static void
-bff_push(struct saved_point **stack, int x, int y)
+bff_push(struct saved_point **stack, int x, int y, int oy)
 {
 	struct saved_point *new = malloc(sizeof(*new));
 
 	if (new) {
 		new->x = x;
 		new->y = y;
+		new->oy = oy;
 		new->next = *stack;
 		*stack = new;
 	}
@@ -9779,15 +9972,14 @@ broken_flood_fill(struct ciolib_pixels *pix, int x, int y, uint32_t edge, uint32
 	if (y >= pix->height)
 		return;
 	foff = y * pix->width + x;
-	if (pix->pixels[foff] == edge)
+	if ((pix->pixels[foff] & 0x80ffffff) == edge)
 		return;
 	if (pix->pixels[foff] & 0x40000000)
 		return;
-	foff = y * pix->width + x;
 
 	// For simplicity sake, we just move left to an edge.
 	if (x > 0) {
-		while (foff > 0 && pix->pixels[foff - 1] != edge) {
+		while (foff > 0 && (pix->pixels[foff - 1] & 0x80ffffff) != edge) {
 			foff--;
 			if (--x == 0)
 				break;
@@ -9812,7 +10004,7 @@ broken_flood_fill(struct ciolib_pixels *pix, int x, int y, uint32_t edge, uint32
 		noff = -1;
 
 	for (;x < pix->width; x++) {
-		if (pix->pixels[foff] == edge)
+		if ((pix->pixels[foff] & 0x80ffffff) == edge)
 			break;
 		if (pix->pixels[foff] & 0x40000000)
 			break;
@@ -9825,15 +10017,15 @@ broken_flood_fill(struct ciolib_pixels *pix, int x, int y, uint32_t edge, uint32
 			// Now, check previous line...
 			if (poff >= 0) {
 				if (prevline) {
-					if (pix->pixels[poff] == edge)
+					if ((pix->pixels[poff] & 0x80ffffff) == edge)
 						prevline = false;
 				}
 				else {
 					if (x != 0 && x != (pix->width - 1)) {
-						if (pix->pixels[poff] != edge) {
+						if ((pix->pixels[poff] & 0x80ffffff) != edge) {
 							prevline = true;
 							if ((pix->pixels[poff] & 0x40000000) == 0)
-								bff_push(stack, x, y - 1);
+								bff_push(stack, x, y - 1, y);
 						}
 					}
 				}
@@ -9841,15 +10033,15 @@ broken_flood_fill(struct ciolib_pixels *pix, int x, int y, uint32_t edge, uint32
 			// And the next line
 			if (noff >= 0) {
 				if (nextline) {
-					if (pix->pixels[noff] == edge)
+					if ((pix->pixels[noff] & 0x80ffffff) == edge)
 						nextline = false;
 				}
 				else {
 					if (x != 0 && x != (pix->width - 1)) {
-						if (pix->pixels[noff] != edge) {
+						if ((pix->pixels[noff] & 0x80ffffff) != edge) {
 							nextline = true;
 							if ((pix->pixels[noff] & 0x40000000) == 0)
-								bff_push(stack, x, y + 1);
+								bff_push(stack, x, y + 1, y);
 						}
 					}
 				}
@@ -9867,7 +10059,7 @@ broken_flood_fill(struct ciolib_pixels *pix, int x, int y, uint32_t edge, uint32
 	if (orig_stack == NULL) {
 		for (this = *stack; this; this = *stack) {
 			*stack = (*stack)->next;
-			broken_flood_fill(pix, this->x, this->y, edge, fillfg, fillbg, iszero, y, stack);
+			broken_flood_fill(pix, this->x, this->y, edge, fillfg, fillbg, iszero, this->oy, stack);
 			free(this);
 		}
 	}
@@ -9930,6 +10122,113 @@ struct point {
 	if (y2 == -1) \
 		break;
 
+static void
+rip_bezier(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int cnt, uint32_t fg)
+{
+	int x, y;
+	int step;
+	int i, j;
+
+	int* targets = malloc((cnt + 2) * 2 * sizeof(*targets));
+	i = 0;
+	targets[i++] = x1;
+	targets[i++] = y1;
+	#pragma clang loop vectorize(enable)
+	for (step = 1; step < cnt; step++) {
+		double tf = ((double)step) / cnt;
+		double tr = ((double)(cnt - step)) / cnt;
+		double tfs = pow(tf, 2);
+		double tfstr = tfs * tr;
+		double tfc = pow(tf, 3);
+		double trs = pow(tr, 2);
+		double tftrs = tf * trs;
+		double trc = pow(tr, 3);
+		x = trc * x1 + 3 * tftrs * x2 + 3 * tfstr * x3 + tfc * x4;
+		y = trc * y1 + 3 * tftrs * y2 + 3 * tfstr * y3 + tfc * y4;
+		targets[i++] = x;
+		targets[i++] = y;
+	}
+	targets[i++] = x4;
+	targets[i++] = y4;
+	for (j = 2; j < i; j += 2) {
+		set_line(targets[j - 2], targets[j - 1], targets[j], targets[j + 1], fg, rip.line_pattern, rip.line_width);
+	}
+}
+
+static void
+rip_poly_bezier(const char *args, bool filled, bool closed)
+{
+	int num = parse_mega(&args[0], 2);
+	int count = parse_mega(&args[2], 2);
+	int i;
+	int type;
+	int x1, y1, x2, y2, x3, y3, x4, y4;
+	int sx = parse_mega(&args[5], 2);
+	int sy = parse_mega(&args[7], 2);
+	int lx = sx;
+	int ly = sy;
+	uint32_t fg = map_rip_color(rip.color);
+	int ch = 4;
+	bool last_was_bez = false;
+
+	if (filled)
+		fg |= 0x40000000;
+
+	for (i = 0; i < num; i++) {
+		type = parse_mega(&args[ch], 1);
+		ch++;
+		switch (type) {
+			case -1:	// Finished?
+				printf("Stopping at %d of %d\n", i, num);
+				break;
+			case 0:	// Straight
+				x1 = parse_mega(&args[ch], 2);
+				y1 = parse_mega(&args[ch+2], 2);
+				ch += 4;
+				if (!last_was_bez)
+					set_line(lx, ly, x1, y1, fg, rip.line_pattern, rip.line_width);
+				lx = x1;
+				ly = y1;
+				last_was_bez = false;
+				break;
+			case 1:	// Bezier
+			case 2:	// Other bezier.
+			case 5:	// ??? yet another bezier...
+			case 6:	// ??? yet another bezier...
+				x1 = parse_mega(&args[ch], 2);
+				y1 = parse_mega(&args[ch+2], 2);
+				x2 = parse_mega(&args[ch+4], 2);
+				y2 = parse_mega(&args[ch+6], 2);
+				x3 = parse_mega(&args[ch+8], 2);
+				y3 = parse_mega(&args[ch+10], 2);
+				ch += 12;
+				if (args[ch] == 0) {
+					x4 = sx;
+					y4 = sy;
+				}
+				else {
+					x4 = parse_mega(&args[ch + 1], 2);
+					y4 = parse_mega(&args[ch + 3], 2);
+				}
+				rip_bezier(x1, y1, x2, y2, x3, y3, x4, y4, count, fg);
+				lx = x4;
+				ly = y4;
+				last_was_bez = true;
+				break;
+			default:
+				printf("TODO: Unhandled poly_bez type %d\n", type);
+				return;
+		}
+	}
+	if (closed && (lx != sx || ly != sy))
+		set_line(lx, ly, sx, sy, fg, rip.line_pattern, rip.line_width);
+	if (filled) {
+		do_fill(false);
+	}
+}
+
+#define ADJUST_LIMIT(name, var) if (var < min##name) min##name = var; if (var > max##name) max##name = var;
+
 static void
 do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
 {
@@ -9943,6 +10242,7 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
 	char cache_path[MAX_PATH + 1];
 	FILE *icn;
 	int *targets;
+	int ex, ey;
 
 	args = parse_string(rawargs);
 
@@ -10105,10 +10405,7 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
 								arg3 = arg1 * vstat.scale_numerator / vstat.scale_denominator;
 							}
 							pthread_mutex_unlock(&vstatlock);
-							if (abs(arg3 - arg1) == 360)
-								full_ellipse(x1, y1, x2, y2, false);
-							else
-								draw_ellipse(x1, y1, x2, y2, arg1, arg3);
+							full_ellipse(x1, y1, x2, y2, arg1, arg3, false, map_rip_color(rip.color));
 							break;
 						case 'B':	// RIP_BAR !|B <x0> <y0> <x1> <y1>
 							/*
@@ -10160,7 +10457,7 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
 							pthread_mutex_unlock(&vstatlock);
 							if (arg1 == 1)
 								arg3 = 1;
-							full_ellipse(x1, y1, arg1, arg3, false);
+							full_ellipse(x1, y1, 0, 360, arg1, arg3, false, map_rip_color(rip.color));
 							break;
 						case 'E':	// RIP_ERASE_VIEW !|E
 							/* This command clears the Graphics Viewport to the current graphics
@@ -10199,6 +10496,7 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
 							if (rip.viewport.ey >= vstat.scrnheight)
 								arg2 = vstat.scrnheight - 1;
 							pthread_mutex_unlock(&vstatlock);
+							struct ciolib_pixels *pix = getpixels(rip.viewport.sx, rip.viewport.sy, rip.viewport.ex, arg2, false);
 							#if 0	// This slow draw is useful for debugging flood fill issues...
 							printf("Flooding %d/%d!\n", x1, y1);
 							uint32_t xx;
@@ -10211,19 +10509,21 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
 							scale_setpixel(x1, y1, xx);
 							}
 							#endif
-							struct ciolib_pixels *pix = getpixels(rip.viewport.sx, rip.viewport.sy, rip.viewport.ex, arg2, false);
 							FREE_AND_NULL(pix->pixelsb);
 							// Blammo goes the stack!
 							fg = map_rip_color(arg1);
 							uint32_t ffg, fbg;
 							ffg = map_rip_color(rip.fill_color);
 							fbg = map_rip_color(0);
+							for (i = 0; i < pix->width * pix->height; i++) {
+								pix->pixels[i] &= 0x80ffffff;
+							}
 							if (x1 < pix->width && y1 < pix->height)
 								broken_flood_fill(pix, x1, y1, fg, ffg, fbg, rip.fill_color == 0, y1, NULL);
 							else
 								puts("TODO: Flood fill off screen");
 							for (i = 0; i < pix->width * pix->height; i++) {
-								pix->pixels[i] &= 0xbfffffff;
+								pix->pixels[i] &= 0x80ffffff;
 							}
 							setpixels(rip.viewport.sx, rip.viewport.sy, rip.viewport.ex, arg2, 0, 0, pix, NULL);
 							freepixels(pix);
@@ -10259,13 +10559,30 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
 								arg3 = arg1 * vstat.scale_numerator / vstat.scale_denominator;
 							}
 							pthread_mutex_unlock(&vstatlock);
-							if (abs(arg3 - arg1) == 360)
-								full_ellipse(x1, y1, x2, y2, false);
-							else
-								draw_ellipse(x1, y1, x2, y2, arg1, arg3);
-							draw_line(x1, y1, x1 + nearbyint(cos(M_PI / 180.0) * arg1) * x2, y1 + nearbyint(sin(M_PI / 180.0) * arg1) * y2);
-							draw_line(x1, y1, x1 + nearbyint(cos(M_PI / 180.0) * arg3) * x2, y1 + nearbyint(sin(M_PI / 180.0) * arg3) * y2);
-							puts("TODO: Needs to fill...");
+							fg = map_rip_color(rip.color) | 0x40000000;
+							full_ellipse(x1, y1, x2, y2, arg1, arg3, false, fg);
+							// This circle may not actually be circular... can't just use sin/cos
+							ex = roundl((x2 * y2) / (sqrt(y2 * y2 + x2 * x2 * pow((tan(arg1 * (M_PI / 180.0))), 2))));
+							if (arg1 > 90 && arg1 < 270)
+								ex = 0 - ex;
+							ex += x1;
+							ey = roundl((x2*y2*tan(arg1 * (M_PI / 180.0)))/(sqrt(y2*y2+x2*x2*pow((tan(arg1 * (M_PI / 180.0))), 2))));
+							if (arg1 > 90 && arg1 < 270)
+								ey = 0 - ey;
+							ey = y1 - ey;
+
+							set_line(x1, y1, ex, ey, fg, 0xffff, rip.line_width);
+							ex = round((x2*y2)/(sqrt(y2*y2+x2*x2*pow((tan(arg3 * (M_PI / 180.0))), 2))));
+							if (arg3 > 90 && arg3 < 270)
+								ex = 0 - ex;
+							ex += x1;
+							ey = roundl((x2*y2*tan(arg3 * (M_PI / 180.0)))/(sqrt(y2*y2+x2*x2*pow((tan(arg3 * (M_PI / 180.0))), 2))));
+							if (arg3 > 90 && arg3 < 270)
+								ey = 0 - ey;
+							ey = y1 - ey;
+							set_line(x1, y1, x1 + nearbyint(cos(M_PI / 180.0) * arg3) * x2, y1 + nearbyint(sin(M_PI / 180.0) * arg3) * y2, fg, 0xffff, rip.line_width);
+							// TODO: Lazy lazy fill...
+							do_fill(false);
 							break;
 						case 'L':	// RIP_LINE !|L <x0> <y0> <x1> <y1>
 							/* This command will draw a line in the current drawing color, using the
@@ -10331,10 +10648,7 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
 							y2 = parse_mega(&args[10], 2);
 							if (x2 < 0 || y2 < 0)
 								break;
-							if (abs(arg2 - arg1) == 360)
-								full_ellipse(x1, y1, x2, y2, false);
-							else
-								draw_ellipse(x1, y1, arg1, arg2, x2, y2);
+							full_ellipse(x1, y1, arg1, arg2, x2, y2, false, map_rip_color(rip.color));
 							break;
 						case 'P':	// RIP_POLYGON !|P <npoints> <x1> <y1> ... <xn> <yn>
 							/* This command will draw a multi-sided closed polygon.  The polygon is
@@ -10608,39 +10922,7 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
 								yp[i] = parse_mega(&args[2 + i * 4], 2);
 							}
 							arg1 = parse_mega(&args[16], 2);
-							x2 = xp[0];
-							y2 = yp[0];
-							targets = malloc((arg1 + 2) * 2 * sizeof(*targets));
-							i = 0;
-							targets[i++] = x2;
-							targets[i++] = y2;
-							// TODO: We should be able to parallelize this...
-							#pragma clang loop vectorize(enable)
-							for (arg2 = 1; arg2 < arg1; arg2++) {
-								double tf = ((double)arg2) / arg1;
-								double tr = ((double)(arg1 - arg2)) / arg1;
-								double tfs = pow(tf, 2);
-								double tfstr = tfs * tr;
-								double tfc = pow(tf, 3);
-								double trs = pow(tr, 2);
-								double tftrs = tf * trs;
-								double trc = pow(tr, 3);
-								x1 = trc * xp[0] + 3 * tftrs * xp[1] + 3 * tfstr * xp[2] + tfc * xp[3];
-								y1 = trc * yp[0] + 3 * tftrs * yp[1] + 3 * tfstr * yp[2] + tfc * yp[3];
-								//x1 = tr * tr * tr * xp[0] + 3 * tf * tr * tr * xp[1] + 3 * tf * tf * tr * xp[2] + tf * tf * tf * xp[3];
-								//y1 = tr * tr * tr * yp[0] + 3 * tf * tr * tr * yp[1] + 3 * tf * tf * tr * yp[2] + tf * tf * tf * yp[3];
-								targets[i++] = x1;
-								targets[i++] = y1;
-								//draw_line(x2, y2, x1, y1);
-								//x2 = x1;
-								//y2 = y1;
-							}
-							targets[i++] = xp[3];
-							targets[i++] = yp[3];
-							for (j = 2; j < i; j += 2) {
-								draw_line(targets[j - 2], targets[j - 1], targets[j], targets[j + 1]);
-							}
-							//draw_line(x1, y1, xp[3], yp[3]);
+							rip_bezier(xp[0], yp[0], xp[1], yp[1], xp[2], yp[2], xp[3], yp[3], arg1, map_rip_color(rip.color));
 							break;
 						case 'a':	// RIP_ONE_PALETTE !|a <color> <value>
 							/* This command changes one color in the 16-color palette.  The color
@@ -10850,20 +11132,41 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
 								break;
 							if (arg2 < arg1)
 								arg2 += 360;
-							// TODO: Use 'o' command ellipse when possible...
-							puts("TODO: Needs to fill...");
-							fg = map_rip_color(rip.color);
-							set_line(x1, y1, x1 + (x2 * cos(arg1 * (M_PI / 180.0))),
-							    y1 - (y2 * sin(arg1 * (M_PI / 180.0))), fg, 0xffff, rip.line_width);
-							for (arg3 = arg1; arg3 < arg2; arg3++) {
-								set_line(x1 + (x2 * cos(arg3 * (M_PI / 180.0))),
-								    y1 - (y2 * sin(arg3 * (M_PI / 180.0))),
-								    x1 + (x2 * cos((arg3 + 1) * (M_PI / 180.0))),
-								    y1 - (y2 * sin((arg3 + 1) * (M_PI / 180.0))),
-								    fg, 0xffff, rip.line_width);
-							}
-							set_line(x1, y1, x1 + (x2 * cos(arg2 * (M_PI / 180.0))),
-							    y1 - (y2 * sin(arg2 * (M_PI / 180.0))), fg, 0xffff, rip.line_width);
+							fg = map_rip_color(rip.color) | 0x40000000;
+							ex = roundl((x2 * y2) / (sqrt(y2 * y2 + x2 * x2 * pow((tan(arg1 * (M_PI / 180.0))), 2))));
+							if (arg1 > 90 && arg1 < 270)
+								ex = 0 - ex;
+							ex += x1;
+							ey = roundl((x2*y2*tan(arg1 * (M_PI / 180.0)))/(sqrt(y2*y2+x2*x2*pow((tan(arg1 * (M_PI / 180.0))), 2))));
+							if (arg1 > 90 && arg1 < 270)
+								ey = 0 - ey;
+							ey = y1 - ey;
+							//ex = x1 + (x2 * cos(arg1 * (M_PI / 180.0)));
+							//ey = y1 - (y2 * sin(arg1 * (M_PI / 180.0)));
+							set_line(x1, y1, ex, ey, fg, 0xffff, rip.line_width);
+							full_ellipse(x1, y1, arg1, arg2, x2, y2, false, fg);
+
+							ex = round((x2*y2)/(sqrt(y2*y2+x2*x2*pow((tan(arg2 * (M_PI / 180.0))), 2))));
+							if (arg2 > 90 && arg2 < 270)
+								ex = 0 - ex;
+							ex += x1;
+							ey = roundl((x2*y2*tan(arg2 * (M_PI / 180.0)))/(sqrt(y2*y2+x2*x2*pow((tan(arg2 * (M_PI / 180.0))), 2))));
+							if (arg2 > 90 && arg2 < 270)
+								ey = 0 - ey;
+							ey = y1 - ey;
+							//ex = x1 + (x2 * cos(arg2 * (M_PI / 180.0)));
+							//ey = y1 - (y2 * sin(arg2 * (M_PI / 180.0)));
+							set_line(x1, y1, ex, ey, fg, 0xffff, rip.line_width);
+							do_fill(false);
+							break;
+						case 'j':	// RIP_POINT (Not in Alpha docs)
+							/* This command will draw a single point in the current line style
+							 */
+							handled = true;
+							if (no_viewport())
+								break;
+							GET_XY();
+							draw_line(x1, y1, x1, y1);
 							break;
 						case 'l':	// RIP_POLYLINE !|l <npoints> <x1> <y1> ... <xn> <yn>
 							/* This command will draw a multi-faceted line.  It is identical in
@@ -10921,11 +11224,10 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
 							rip.y = y1;
 							break;
 						case 'o':	// RIP_FILLED_OVAL
-							// TODO: Various issues... mose especially a 1x1 isn't a + shape, but a - shape.
 						{
 							handled = true;
 							GET_XY();
-							full_ellipse(x1, y1, parse_mega(&args[4], 2), parse_mega(&args[6], 2), true);
+							full_ellipse(x1, y1, 0, 360, parse_mega(&args[4], 2), parse_mega(&args[6], 2), true, map_rip_color(rip.color));
 							break;
 						}
 						case 'p':	// RIP_FILL_POLYGON !|p <npoints> <x1> <y1> ... <xn> <yn>
@@ -10941,8 +11243,51 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
 							 *        does not utilize Write Mode, but the outline of the polygon
 							 *        does.
 							 */
+#if 0
+							if (rip.version == RIP_VERSION_3) {
+								handled = true;
+								if (no_viewport())
+									break;
+								fg = map_rip_color(rip.color) | 0x40000000;
+								arg1 = parse_mega(&args[0], 2);
+								for (i = 0; i < arg1; i++) {
+									if (i == 0) {
+										x1 = parse_mega(&args[2], 2);
+										if (x1 == -1)
+											break;
+										y1 = parse_mega(&args[4], 2);
+										if (y1 == -1)
+											break;
+									}
+									else {
+										x2 = parse_mega(&args[2 + i * 4], 2);
+										if (x2 == -1)
+											break;
+										y2 = parse_mega(&args[4 + i * 4], 2);
+										if (y2 == -1)
+											break;
+										set_line(x1, y1, x2, y2, fg
+										    , rip.line_pattern, rip.line_width);
+										x1 = x2;
+										y1 = y2;
+									}
+								}
+								if (i > 0) {
+									x2 = parse_mega(&args[2], 2);
+									if (x1 == -1)
+										break;
+									y2 = parse_mega(&args[4], 2);
+									if (y1 == -1)
+										break;
+									set_line(x1, y1, x2, y2, fg
+									    , rip.line_pattern, rip.line_width);
+									do_fill(false);
+								}
+								break;
+							}
+#endif
 							// DOES NOT DRAW LINES IN COLOR ZERO!!!
-							// Not bug-compliant with RIPterm
+							// Not bug-compliant with RIPterm (But getting very close!)
 							handled = true;
 							if (no_viewport())
 								break;
@@ -11095,6 +11440,10 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
 							rip.fill_pattern[7] = parse_mega(&args[14], 2) & 0xff;
 							rip.fill_color = arg1;
 							break;
+						case 't':	// RIP_POLY_BEZIER_LINE (v2.A2)
+							handled = true;
+							rip_poly_bezier(args, false, false);
+							break;
 						case 'v':	// RIP_VIEWPORT !|v <x0> <y0> <x1> <y1>
 							/* This command defines the (X,Y) pixel boundaries of the RIPscrip
 							 * graphics window, which will contain all RIPscrip graphics output.
@@ -11243,6 +11592,14 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
 								hold_update = arg3;
 							}
 							break;
+						case 'x':	// RIP_FILLED_POLY_BEZIER (v2.A2)
+							handled = true;
+							rip_poly_bezier(args, true, true);
+							break;
+						case 'z':	// RIP_POLY_BEZIER (v2.A1)
+							handled = true;
+							rip_poly_bezier(args, false, true);
+							break;
 					}
 			}
 			break;
@@ -12923,7 +13280,7 @@ do_rip_string(const char *buf, size_t len)
 		switch (ds) {
 			case NEED_BANG:
 				ch = next_char(buf, &pos);
-				if ((ch == -1 && buf[pos] == '!') || ch == '\x01' || ch == '\x02')
+				if ((ch == -1 && buf[pos] == '!') || ch == '\x01' || ch == '\x02' || ch == '!')
 					ds = NEED_PIPE;
 				break;
 			case NEED_PIPE:
@@ -13364,7 +13721,7 @@ do_skypix(char *buf, size_t len)
 			olw = rip.line_width;
 			rip.fill_color = (cterm->attr >> 4) & 0x0f;
 			rip.line_width = 1;
-			full_ellipse(argv[1], argv[2], argv[3], argv[4], true);
+			full_ellipse(argv[1], argv[2], 0, 360, argv[3], argv[4], true, map_rip_color(rip.color));
 			rip.fill_color = oc;
 			rip.line_width = olw;
 			break;
-- 
GitLab