libansilove

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

pcboard.c (4655B)


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