libansilove

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

pcboard.c (4886B)


      1 /*
      2  * pcboard.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 <stdint.h>
     17 #include <stdlib.h>
     18 #include <string.h>
     19 #include "ansilove.h"
     20 #include "config.h"
     21 #include "drawchar.h"
     22 #include "fonts.h"
     23 #include "output.h"
     24 
     25 #ifndef HAVE_REALLOCARRAY
     26 #include "reallocarray.h"
     27 #endif
     28 
     29 #define PCB_BUFFER_SIZE 4096
     30 
     31 #define STATE_TEXT	0
     32 #define STATE_SEQUENCE	1
     33 #define STATE_END	2
     34 
     35 /* Character structure */
     36 struct pcbChar {
     37 	uint32_t column;
     38 	uint32_t row;
     39 	uint32_t background;
     40 	uint32_t foreground;
     41 	uint8_t character;
     42 };
     43 
     44 int
     45 ansilove_pcboard(struct ansilove_ctx *ctx, struct ansilove_options *options)
     46 {
     47 	uint8_t *cursor, state = STATE_TEXT;
     48 	uint32_t background = '0', foreground = '7';
     49 	uint32_t column = 0, row = 0, rowMax = 0;
     50 	uint32_t width, height;
     51 	uint32_t colors[16];
     52 	size_t loop = 0, structIndex = 0;
     53 	struct fontStruct fontData;
     54 
     55 	/* PCB buffer structure array definition */
     56 	struct pcbChar *ptr, *pcboard_buffer = NULL;
     57 
     58 	size_t pcb_buffer_size = PCB_BUFFER_SIZE;
     59 
     60 	/* libgd image pointers */
     61 	gdImagePtr canvas;
     62 
     63 	if (ctx == NULL || options == NULL) {
     64 		if (ctx)
     65 			ctx->error = ANSILOVE_INVALID_PARAM;
     66 
     67 		return -1;
     68 	}
     69 
     70 	if (!ctx->length) {
     71 		ctx->error = ANSILOVE_FORMAT_ERROR;
     72 		return -1;
     73 	}
     74 
     75 	if (options->bits != 8 && options->bits !=9) {
     76 		ctx->error = ANSILOVE_RANGE_ERROR;
     77 		return -1;
     78 	}
     79 
     80 	options->columns = options->columns ? options->columns : 80;
     81 	uint16_t columns = options->columns;
     82 
     83 	if (columns < 1 || columns > 4096) {
     84 		ctx->error = ANSILOVE_RANGE_ERROR;
     85 		return -1;
     86 	}
     87 
     88 	/* font selection */
     89 	memset(&fontData, 0, sizeof(struct fontStruct));
     90 	select_font(&fontData, options->font);
     91 
     92 	/* PCB buffer dynamic memory allocation */
     93 	pcboard_buffer = malloc(pcb_buffer_size * sizeof(struct pcbChar));
     94 
     95 	if (pcboard_buffer == NULL) {
     96 		ctx->error = ANSILOVE_MEMORY_ERROR;
     97 		return -1;
     98 	}
     99 
    100 	while (loop < ctx->length) {
    101 		cursor = &ctx->buffer[loop];
    102 
    103 		if (column == columns) {
    104 			row++;
    105 			column = 0;
    106 		}
    107 
    108 		switch (state) {
    109 		case STATE_TEXT:
    110 			switch (*cursor) {
    111 			case LF:
    112 				row++;
    113 				column = 0;
    114 				break;
    115 			case CR:
    116 				break;
    117 			case TAB:
    118 				column += 8;
    119 				break;
    120 			case SUB:
    121 				state = STATE_END;
    122 				break;
    123 			case '@':
    124 				/* PCB sequence */
    125 				state = STATE_SEQUENCE;
    126 				break;
    127 			default:
    128 				/* record number of lines used */
    129 				if (row > rowMax)
    130 					rowMax = row;
    131 
    132 				/* reallocate structure array memory */
    133 				if (structIndex == pcb_buffer_size) {
    134 					pcb_buffer_size += PCB_BUFFER_SIZE;
    135 
    136 					ptr = reallocarray(pcboard_buffer, pcb_buffer_size, sizeof(struct pcbChar));
    137 					if (ptr == NULL) {
    138 						ctx->error = ANSILOVE_MEMORY_ERROR;
    139 						goto error;
    140 					}
    141 
    142 					pcboard_buffer = ptr;
    143 				}
    144 
    145 				/* write current character in pcbChar struct */
    146 				pcboard_buffer[structIndex] = (struct pcbChar) {
    147 					.column = column,
    148 					.row = row,
    149 					.background = pcb_colors[background],
    150 					.foreground = pcb_colors[foreground],
    151 					.character = *cursor
    152 				};
    153 
    154 				column++;
    155 				structIndex++;
    156 			};
    157 			loop++;
    158 			break;
    159 		case STATE_SEQUENCE:
    160 			if (*cursor == 'X') {
    161 				/* set graphics rendition */
    162 				if (loop + 2 < ctx->length) {
    163 					background = *++cursor;
    164 					foreground = *++cursor;
    165 
    166 					loop += 3;
    167 				}
    168 
    169 				if (background >= PCB_COLORS ||
    170 				    foreground >= PCB_COLORS) {
    171 					ctx->error = ANSILOVE_FORMAT_ERROR;
    172 					goto error;
    173 				}
    174 			}
    175 
    176 			if (loop + 3 < ctx->length && !memcmp(cursor, "CLS@", 4)) {
    177 				/* erase display */
    178 				column = 0;
    179 				row = 0;
    180 
    181 				rowMax = 0;
    182 
    183 				/* reset pcboard buffer */
    184 				structIndex = 0;
    185 
    186 				loop += 4;
    187 			}
    188 
    189 			state = STATE_TEXT;
    190 			break;
    191 		case STATE_END:
    192 			loop = ctx->length;
    193 			break;
    194 		}
    195 	}
    196 	rowMax++;
    197 
    198 	width = columns * options->bits;
    199 	height = rowMax * fontData.height;
    200 
    201 	if (!width || !height) {
    202 		ctx->error = ANSILOVE_FORMAT_ERROR;
    203 		goto error;
    204 	}
    205 
    206 	/* allocate buffer image memory */
    207 	canvas = gdImageCreate(width, height);
    208 
    209 	if (!canvas) {
    210 		ctx->error = ANSILOVE_GD_ERROR;
    211 		goto error;
    212 	}
    213 
    214 	/* allocate color palette */
    215 	for (size_t i = 0; i < 16; i++)
    216 		colors[i] = gdImageColorAllocate(canvas, ansi_palette_red[i],
    217 		    ansi_palette_green[i], ansi_palette_blue[i]);
    218 
    219 	/* render PCB */
    220 	for (loop = 0; loop < structIndex; loop++) {
    221 		drawchar(canvas, fontData.font_data, options->bits, fontData.height,
    222 		    pcboard_buffer[loop].column,
    223 		    pcboard_buffer[loop].row,
    224 		    colors[pcboard_buffer[loop].background],
    225 		    colors[pcboard_buffer[loop].foreground],
    226 		    pcboard_buffer[loop].character);
    227 	}
    228 
    229 	/* create output image */
    230 	if (output(ctx, options, canvas) != 0)
    231 		goto error;
    232 
    233 	free(pcboard_buffer);
    234 	return 0;
    235 
    236 error:
    237 	free(pcboard_buffer);
    238 	return -1;
    239 }