libansilove

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

ansi.c (14032B)


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