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 }