libansilove

Library for converting ANSI, ASCII, and other formats to PNG
Log | Files | Refs | README | LICENSE

ansi.c (14075B)


      1 /*
      2  * ansi.c
      3  * libansilove 1.3.1
      4  * https://www.ansilove.org
      5  *
      6  * Copyright (c) 2011-2022 Stefan Vogt, Brian Cassidy, and Frederic Cambus
      7  * All rights reserved.
      8  *
      9  * libansilove is licensed under the BSD 2-Clause license.
     10  * See LICENSE file for details.
     11  *
     12  * SPDX-License-Identifier: BSD-2-Clause
     13  */
     14 
     15 #include <gd.h>
     16 #include <limits.h>
     17 #include <math.h>
     18 #include <stdbool.h>
     19 #include <stdint.h>
     20 #include <stdlib.h>
     21 #include <string.h>
     22 #include "ansilove.h"
     23 #include "config.h"
     24 #include "drawchar.h"
     25 #include "fonts.h"
     26 #include "output.h"
     27 
     28 #ifndef HAVE_STRTONUM
     29 #include "strtonum.h"
     30 #endif
     31 
     32 #ifndef HAVE_REALLOCARRAY
     33 #include "reallocarray.h"
     34 #endif
     35 
     36 #define ANSI_SEQUENCE_MAX_LENGTH 14
     37 #define ANSI_BUFFER_SIZE 65536
     38 
     39 #define STATE_TEXT	0
     40 #define STATE_SEQUENCE	1
     41 #define STATE_END	2
     42 
     43 /* Character structure */
     44 struct ansiChar {
     45 	int32_t column;
     46 	int32_t row;
     47 	uint32_t background;
     48 	uint32_t foreground;
     49 	uint8_t character;
     50 };
     51 
     52 int
     53 ansilove_ansi(struct ansilove_ctx *ctx, struct ansilove_options *options)
     54 {
     55 	const char *errstr;
     56 
     57 	/* ANSi processing loops */
     58 	size_t loop = 0, ansi_sequence_loop;
     59 
     60 	/* character definitions */
     61 	uint8_t character;
     62 	uint8_t *cursor, state = STATE_TEXT;
     63 	uint8_t ansi_sequence_character;
     64 
     65 	/* default color values */
     66 	uint32_t background = 0, foreground = 7;
     67 	uint32_t background24 = 0, foreground24 = 0;
     68 	uint32_t colors[16];
     69 
     70 	/* text attributes */
     71 	bool bold = false, blink = false, invert = false;
     72 
     73 	/* positions */
     74 	int32_t column = 0, row = 0, columnMax = 0, rowMax = 0;
     75 	int32_t saved_row = 0, saved_column = 0;
     76 
     77 	/* sequence parsing variables */
     78 	uint32_t maxlength;
     79 	uint32_t seqValue, seq_line, seq_column;
     80 	char *seqGrab = NULL;
     81 	char *seqTok = NULL;
     82 
     83 	/* ANSi buffer structure array definition */
     84 	size_t structIndex = 0;
     85 	struct ansiChar *ptr, *ansi_buffer = NULL;
     86 	struct fontStruct fontData;
     87 
     88 	size_t ansi_buffer_size = ANSI_BUFFER_SIZE;
     89 
     90 	/* libgd image pointers */
     91 	gdImagePtr canvas;
     92 
     93 	if (ctx == NULL || options == NULL) {
     94 		if (ctx)
     95 			ctx->error = ANSILOVE_INVALID_PARAM;
     96 
     97 		return -1;
     98 	}
     99 
    100 	if (!ctx->length) {
    101 		ctx->error = ANSILOVE_FORMAT_ERROR;
    102 		return -1;
    103 	}
    104 
    105 	if (options->bits != 8 && options->bits !=9) {
    106 		ctx->error = ANSILOVE_RANGE_ERROR;
    107 		return -1;
    108 	}
    109 
    110 	/* Default to 80 columns if columns option wasn't set */
    111 	options->columns = options->columns ? options->columns : 80;
    112 
    113 	int16_t columns = options->columns;
    114 
    115 	if (columns < 1 || columns > 4096) {
    116 		ctx->error = ANSILOVE_RANGE_ERROR;
    117 		return -1;
    118 	}
    119 
    120 	bool ced = false;
    121 	bool workbench = false;
    122 
    123 	/* font selection */
    124 	memset(&fontData, 0, sizeof(struct fontStruct));
    125 	select_font(&fontData, options->font);
    126 
    127 	switch (options->mode) {
    128 	case ANSILOVE_MODE_CED:
    129 		ced = true;
    130 		break;
    131 	case ANSILOVE_MODE_WORKBENCH:
    132 		workbench = true;
    133 		break;
    134 	}
    135 
    136 	/* ANSi buffer dynamic memory allocation */
    137 	ansi_buffer = malloc(ansi_buffer_size * sizeof(struct ansiChar));
    138 
    139 	if (ansi_buffer == NULL) {
    140 		ctx->error = ANSILOVE_MEMORY_ERROR;
    141 		return -1;
    142 	}
    143 
    144 	/* ANSi interpreter */
    145 	while (loop < ctx->length) {
    146 		cursor = &ctx->buffer[loop];
    147 
    148 		if (column == options->columns) {
    149 			row++;
    150 			column = 0;
    151 		}
    152 
    153 		switch (state) {
    154 		case STATE_TEXT:
    155 			switch (*cursor) {
    156 			case LF:
    157 				row++;
    158 				column = 0;
    159 				break;
    160 			case CR:
    161 				break;
    162 			case TAB:
    163 				column += 8;
    164 				break;
    165 			case SUB:
    166 				state = STATE_END;
    167 				break;
    168 			case ESC:
    169 				/* ANSi sequence */
    170 				if ((loop+1 < ctx->length) && ctx->buffer[loop + 1] == 91) {
    171 					state = STATE_SEQUENCE;
    172 					loop++;
    173 				}
    174 				break;
    175 			default:
    176 				/* record number of columns and lines used */
    177 				if (column > columnMax)
    178 					columnMax = column;
    179 
    180 				if (row > rowMax)
    181 					rowMax = row;
    182 
    183 				/* reallocate structure array memory */
    184 				if (structIndex == ansi_buffer_size) {
    185 					ansi_buffer_size += ANSI_BUFFER_SIZE;
    186 
    187 					ptr = reallocarray(ansi_buffer, ansi_buffer_size, sizeof(struct ansiChar));
    188 					if (ptr == NULL) {
    189 						ctx->error = ANSILOVE_MEMORY_ERROR;
    190 						goto error;
    191 					}
    192 
    193 					ansi_buffer = ptr;
    194 				}
    195 
    196 				/* write current character in ansiChar structure */
    197 				if (invert) {
    198 					ansi_buffer[structIndex].background = foreground % 8;
    199 					ansi_buffer[structIndex].foreground = background + (foreground & 8);
    200 				} else {
    201 					ansi_buffer[structIndex].background =
    202 					    background24 ? background24 : background;
    203 
    204 					ansi_buffer[structIndex].foreground =
    205 					    foreground24 ? foreground24 : foreground;
    206 				}
    207 				ansi_buffer[structIndex].character = *cursor;
    208 				ansi_buffer[structIndex].column = column;
    209 				ansi_buffer[structIndex].row = row;
    210 
    211 				structIndex++;
    212 				column++;
    213 			}
    214 			break;
    215 		case STATE_SEQUENCE:
    216 			maxlength = fmin(ctx->length - loop, ANSI_SEQUENCE_MAX_LENGTH);
    217 			for (ansi_sequence_loop = 0; ansi_sequence_loop < maxlength; ansi_sequence_loop++) {
    218 				ansi_sequence_character = ctx->buffer[loop + ansi_sequence_loop];
    219 
    220 				/* cursor position */
    221 				if (ansi_sequence_character == 'H' || ansi_sequence_character == 'f') {
    222 					/* create substring from the sequence's content */
    223 					seq_line = 1;
    224 					seq_column = 1;
    225 					seqGrab = strndup((char *)cursor, ansi_sequence_loop);
    226 					if (!seqGrab) {
    227 						ctx->error = ANSILOVE_MEMORY_ERROR;
    228 						goto error;
    229 					}
    230 
    231 					if (!strncmp(seqGrab, ";", 1)) {
    232 						seq_line = 1;
    233 						seqTok = strtok(seqGrab, ";");
    234 
    235 						if (seqTok)
    236 							seq_column = strtonum(seqTok, 0, UINT32_MAX, &errstr);
    237 					} else {
    238 						seqTok = strtok(seqGrab, ";");
    239 						if (seqTok)
    240 							seq_line = strtonum(seqTok, 0, UINT32_MAX, &errstr);
    241 
    242 						seqTok = strtok(NULL, ";");
    243 						if (seqTok)
    244 							seq_column = strtonum(seqTok, 0, UINT32_MAX, &errstr);
    245 					}
    246 
    247 					/* set the positions */
    248 					row = seq_line-1;
    249 					column = seq_column-1;
    250 
    251 					loop += ansi_sequence_loop;
    252 					free(seqGrab);
    253 					break;
    254 				}
    255 
    256 				/* cursor up */
    257 				if (ansi_sequence_character == 'A') {
    258 					/* create substring from the sequence's content */
    259 					seqGrab = strndup((char *)cursor, ansi_sequence_loop);
    260 					if (!seqGrab) {
    261 						ctx->error = ANSILOVE_MEMORY_ERROR;
    262 						goto error;
    263 					}
    264 
    265 					/* now get escape sequence's position value */
    266 					uint32_t seq_line = strtonum(seqGrab, 0, UINT32_MAX, &errstr);
    267 					free(seqGrab);
    268 
    269 					row -= seq_line ? seq_line : 1;
    270 
    271 					if (row < 0)
    272 						row = 0;
    273 
    274 					loop += ansi_sequence_loop;
    275 					break;
    276 				}
    277 
    278 				/* cursor down */
    279 				if (ansi_sequence_character == 'B') {
    280 					/* create substring from the sequence's content */
    281 					seqGrab = strndup((char *)cursor, ansi_sequence_loop);
    282 					if (!seqGrab) {
    283 						ctx->error = ANSILOVE_MEMORY_ERROR;
    284 						goto error;
    285 					}
    286 
    287 					/* now get escape sequence's position value */
    288 					uint32_t seq_line = strtonum(seqGrab, 0, UINT32_MAX, &errstr);
    289 					free(seqGrab);
    290 
    291 					row += seq_line ? seq_line : 1;
    292 
    293 					loop += ansi_sequence_loop;
    294 					break;
    295 				}
    296 
    297 				/* cursor forward */
    298 				if (ansi_sequence_character == 'C') {
    299 					/* create substring from the sequence's content */
    300 					seqGrab = strndup((char *)cursor, ansi_sequence_loop);
    301 					if (!seqGrab) {
    302 						ctx->error = ANSILOVE_MEMORY_ERROR;
    303 						goto error;
    304 					}
    305 
    306 					/* now get escape sequence's position value */
    307 					uint32_t seq_column = strtonum(seqGrab, 0, UINT32_MAX, &errstr);
    308 					free(seqGrab);
    309 
    310 					column += seq_column ? seq_column : 1;
    311 
    312 					if (column > options->columns)
    313 						column = options->columns;
    314 
    315 					loop += ansi_sequence_loop;
    316 					break;
    317 				}
    318 
    319 				/* cursor backward */
    320 				if (ansi_sequence_character == 'D') {
    321 					/* create substring from the sequence's content */
    322 					seqGrab = strndup((char *)cursor, ansi_sequence_loop);
    323 					if (!seqGrab) {
    324 						ctx->error = ANSILOVE_MEMORY_ERROR;
    325 						goto error;
    326 					}
    327 
    328 					/* now get escape sequence's content length */
    329 					uint32_t seq_column = strtonum(seqGrab, 0, UINT32_MAX, &errstr);
    330 					free(seqGrab);
    331 
    332 					column -= seq_column ? seq_column : 1;
    333 
    334 					if (column < 0)
    335 						column = 0;
    336 
    337 					loop += ansi_sequence_loop;
    338 					break;
    339 				}
    340 
    341 				/* save cursor position */
    342 				if (ansi_sequence_character == 's') {
    343 					saved_row = row;
    344 					saved_column = column;
    345 
    346 					loop += ansi_sequence_loop;
    347 					break;
    348 				}
    349 
    350 				/* restore cursor position */
    351 				if (ansi_sequence_character == 'u') {
    352 					row = saved_row;
    353 					column = saved_column;
    354 
    355 					loop += ansi_sequence_loop;
    356 					break;
    357 				}
    358 
    359 				/* erase display */
    360 				if (ansi_sequence_character == 'J') {
    361 					/* create substring from the sequence's content */
    362 					seqGrab = strndup((char *)cursor, ansi_sequence_loop);
    363 					if (!seqGrab) {
    364 						ctx->error = ANSILOVE_MEMORY_ERROR;
    365 						goto error;
    366 					}
    367 
    368 					/* convert grab to an integer */
    369 					uint32_t eraseDisplayInt = strtonum(seqGrab, 0, UINT32_MAX, &errstr);
    370 					free(seqGrab);
    371 
    372 					if (eraseDisplayInt == 2) {
    373 						column = 0;
    374 						row = 0;
    375 
    376 						columnMax = 0;
    377 						rowMax = 0;
    378 
    379 						/* reset ansi buffer */
    380 						structIndex = 0;
    381 					}
    382 					loop += ansi_sequence_loop;
    383 					break;
    384 				}
    385 
    386 				/* set graphics mode */
    387 				if (ansi_sequence_character == 'm') {
    388 					/* create substring from the sequence's content */
    389 					seqGrab = strndup((char *)cursor, ansi_sequence_loop);
    390 					if (!seqGrab) {
    391 						ctx->error = ANSILOVE_MEMORY_ERROR;
    392 						goto error;
    393 					}
    394 
    395 					seqTok = strtok(seqGrab, ";");
    396 					while (seqTok) {
    397 						seqValue = strtonum(seqTok, 0, UINT32_MAX, &errstr);
    398 
    399 						if (seqValue == 0) {
    400 							background = 0;
    401 							background24 = 0;
    402 							foreground = 7;
    403 							foreground24 = 0;
    404 							bold = false;
    405 							blink = false;
    406 							invert = false;
    407 						}
    408 
    409 						if (seqValue == 1) {
    410 							if (!workbench) {
    411 								foreground += 8;
    412 							}
    413 							bold = true;
    414 							foreground24 = 0;
    415 						}
    416 
    417 						if (seqValue == 5) {
    418 							if (!workbench && options->icecolors)
    419 								background += 8;
    420 
    421 							blink = true;
    422 							background24 = 0;
    423 						}
    424 
    425 						if (seqValue == 7)
    426 							invert = true;
    427 
    428 						if (seqValue == 27)
    429 							invert = false;
    430 
    431 						if (seqValue > 29 && seqValue < 38) {
    432 							foreground = seqValue - 30;
    433 							foreground24 = 0;
    434 
    435 							if (bold)
    436 								foreground += 8;
    437 						}
    438 
    439 						if (seqValue > 39 && seqValue < 48) {
    440 							background = seqValue - 40;
    441 							background24 = 0;
    442 
    443 							if (blink && options->icecolors)
    444 								background += 8;
    445 						}
    446 
    447 						seqTok = strtok(NULL, ";");
    448 					}
    449 
    450 					loop += ansi_sequence_loop;
    451 					free(seqGrab);
    452 					break;
    453 				}
    454 
    455 				/* cursor (de)activation (Amiga ANSi) */
    456 				if (ansi_sequence_character == 'p') {
    457 					loop += ansi_sequence_loop;
    458 					break;
    459 				}
    460 
    461 				/* skipping set mode and reset mode sequences */
    462 				if (ansi_sequence_character == 'h' || ansi_sequence_character == 'l') {
    463 					loop += ansi_sequence_loop;
    464 					break;
    465 				}
    466 
    467 				/* skipping erase in line (EL) sequences */
    468 				if (ansi_sequence_character == 'K') {
    469 					loop += ansi_sequence_loop;
    470 					break;
    471 				}
    472 
    473 				/* PabloDraw 24-bit ANSI sequences */
    474 				if (ansi_sequence_character == 't') {
    475 					uint32_t color_R = 0, color_G = 0, color_B = 0;
    476 
    477 					/* create substring from the sequence's content */
    478 					seqGrab = strndup((char *)cursor, ansi_sequence_loop);
    479 					if (!seqGrab) {
    480 						ctx->error = ANSILOVE_MEMORY_ERROR;
    481 						goto error;
    482 					}
    483 
    484 					seqTok = strtok(seqGrab, ";");
    485 					if (seqTok) {
    486 						seqValue = strtonum(seqTok, 0, UCHAR_MAX, &errstr);
    487 
    488 						seqTok = strtok(NULL, ";");
    489 						color_R = seqTok ? strtonum(seqTok, 0, UCHAR_MAX, &errstr) & 0xff : 0;
    490 						seqTok = strtok(NULL, ";");
    491 						color_G = seqTok ? strtonum(seqTok, 0, UCHAR_MAX, &errstr) & 0xff : 0;
    492 						seqTok = strtok(NULL, ";");
    493 						color_B = seqTok ? strtonum(seqTok, 0, UCHAR_MAX, &errstr) & 0xff : 0;
    494 
    495 						switch (seqValue) {
    496 						case 0:
    497 							background24 = color_R << 16 | color_G << 8 | color_B;
    498 							break;
    499 						case 1:
    500 							foreground24 = color_R << 16 | color_G << 8 | color_B;
    501 							break;
    502 						}
    503 
    504 						options->truecolor = true;
    505 					}
    506 
    507 					loop += ansi_sequence_loop;
    508 					free(seqGrab);
    509 					break;
    510 				}
    511 			}
    512 			state = STATE_TEXT;
    513 			break;
    514 		case STATE_END:
    515 			loop = ctx->length;
    516 			break;
    517 		}
    518 
    519 		loop++;
    520 	}
    521 
    522 	/* allocate image buffer memory */
    523 	columnMax++;
    524 	rowMax++;
    525 
    526 	if (ced)
    527 		columns = 78;
    528 
    529 	if (options->diz)
    530 		columns = fmin(columnMax, options->columns);
    531 
    532 	uint32_t width = columns * options->bits;
    533 	uint32_t height = rowMax * fontData.height;
    534 
    535 	if (!width || !height) {
    536 		ctx->error = ANSILOVE_FORMAT_ERROR;
    537 		goto error;
    538 	}
    539 
    540 	/* create that damn thingy */
    541 	canvas = options->truecolor ?
    542 	    gdImageCreateTrueColor(width, height) :
    543 	    gdImageCreate(width, height);
    544 
    545 	if (!canvas) {
    546 		ctx->error = ANSILOVE_GD_ERROR;
    547 		goto error;
    548 	}
    549 
    550 	uint32_t ced_background = 0, ced_foreground = 0;
    551 
    552 	if (ced) {
    553 		ced_background = gdImageColorAllocate(canvas, 170, 170, 170);
    554 		ced_foreground = gdImageColorAllocate(canvas, 0, 0, 0);
    555 		gdImageFill(canvas, 0, 0, ced_background);
    556 	} else if (workbench) {
    557 		for (size_t i = 0; i < 16; i++)
    558 			colors[i] = gdImageColorAllocate(canvas,
    559 			    workbench_palette_red[i],
    560 			    workbench_palette_green[i],
    561 			    workbench_palette_blue[i]);
    562 	} else {
    563 		/* Allocate standard ANSi color palette */
    564 
    565 		for (size_t i = 0; i < 16; i++)
    566 			colors[i] = gdImageColorAllocate(canvas,
    567 			    ansi_palette_red[i], ansi_palette_green[i],
    568 			    ansi_palette_blue[i]);
    569 	}
    570 
    571 	/* render ANSi */
    572 	for (loop = 0; loop < structIndex; loop++) {
    573 		/* grab ANSi char from our structure array */
    574 		background = ansi_buffer[loop].background;
    575 		foreground = ansi_buffer[loop].foreground;
    576 		character = ansi_buffer[loop].character;
    577 		column = ansi_buffer[loop].column;
    578 		row = ansi_buffer[loop].row;
    579 
    580 		if (ced) {
    581 			background = ced_background;
    582 			foreground = ced_foreground;
    583 		} else {
    584 			if (background < 16)
    585 				background = colors[background];
    586 
    587 			if (foreground < 16)
    588 				foreground = colors[foreground];
    589 		}
    590 
    591 		drawchar(canvas, fontData.font_data, options->bits,
    592 		    fontData.height, column, row, background, foreground,
    593 		    character);
    594 	}
    595 
    596 	/* create output image */
    597 	if (output(ctx, options, canvas) != 0)
    598 		goto error;
    599 
    600 	/* free memory */
    601 	free(ansi_buffer);
    602 	return 0;
    603 
    604 error:
    605 	free(ansi_buffer);
    606 	return -1;
    607 }